以下测试代码在VS中正确执行调试或发布,以及GCC中。它也适用于带调试的ICC,但在启用优化时却没有(-O2
)。
#include <cstdio>
class tClassA{
public:
int m_first, m_last;
tClassA() : m_first(0), m_last(0) {}
~tClassA() {}
bool isEmpty() const {return (m_first == m_last);}
void updateFirst() {m_first = m_first + 1;}
void updateLast() {m_last = m_last + 1;}
void doSomething() {printf("should not reach here\r\n");}
};
int main() {
tClassA q;
while(true) {
while(q.isEmpty()) ;
q.doSomething();
}
return 1;
}
它应该停在while(q.isEmpty())
。但是,当在ICC(发布)下启用-O2
时,它会无限地开始“doSomething”。
由于这是单线程程序和 isEmpty()
应该被评估为true
,我发现ICC没有理由这样做?我错过了什么吗?
答案 0 :(得分:10)
因为while (q.isEmpty()) ;
循环不包含任何可能导致外部可见副作用的语句,所以整个循环优化不存在。这与以下原因相同:
for (int i = 0; i < 10; i++)
;
只要i
不是volatile
(存储到volatile
个对象是程序“外部可见”效果的一部分,就可以优化不存在。
在C语言中,对于是否允许以这种方式优化无限循环(我不知道C ++的情况如何),它实际上是一个突出的争论焦点。据我所知,在这个问题上从来没有达成共识 - 聪明且知识渊博的人已经采取了双方。
答案 1 :(得分:2)
这听起来像个错误。这是一个(相当疯狂的)猜测可能导致它的原因......
内联后,它看到了:
while (q.m_first == q.m_last) /* do nothing */ ;
do_something();
并且do nothing repeatedly ; do something
的任何序列都可以翻译为“做某事”。如果重复的部分是无穷无尽的(如在这种情况下),这就会失败。但也许他们没有测试他们对有意无限循环的例子的编译; - )。
答案 2 :(得分:1)
您构建和运行的实际代码是否有可能在while(q.isEmpty())
之后缺少分号?这肯定会导致下一行被无限调用。
答案 3 :(得分:1)
稍微说一下,这个版本的icc可以满足您的需求。也就是说,它永远不会调用doSomething()
。
[9:41am][wlynch@computer /tmp] icc --version
icc (ICC) 11.0 20081105
答案 4 :(得分:1)
C ++标准允许删除没有副作用的循环,即使它们没有终止:
人们普遍认为它是 允许转型很重要 潜在的非终止循环 (例如,通过合并两个循环 迭代相同的潜在 无限集,或通过消除 无副作用的循环),即使是那样 可能没有其他理由 从来没有第一个循环的情况 终止。 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2429.htm
请参阅此处的讨论: http://blog.regehr.org/archives/161
答案 5 :(得分:0)
您最好的选择是将生成的二进制步骤放入其中并拆分主函数并查看生成的程序集。不是说你将能够看到一个错误,但你可以看到是否有一些东西被优化了。
答案 6 :(得分:-3)
我认为它可能是你的gcc版本。要编译4.4.2下的程序,它的工作原理应该完全正确。