C++编译错误?
我有以下代码:
#include <iostream>
#include <complex>
using namespace std;
int main() {
complex<int> delta;
complex<int> mc[4] = {0};
for(int di = 0; di < 4; di++, delta = mc[di]) {
cout << di << endl;
}
return 0;
}
我希望它输出0, 1, 2, 3"并停止,但它输出了无休止的0, 1, 2, 3, 4, 5, ....."系列
I expect it to output "0, 1, 2, 3" and stop, but it outputs an endless series of "0, 1, 2, 3, 4, 5, ....."
看起来比较 di<4
效果不佳,总是返回 true.
It looks like the comparison di<4
doesn't work well and always returns true.
如果我只是注释掉 ,delta=mc[di]
,我会像往常一样得到0, 1, 2, 3".无辜的分配有什么问题?
If I just comment out ,delta=mc[di]
, I get "0, 1, 2, 3" as normal. What's the problem with the innocent assignment?
我正在使用带有 -O2 选项的 Ideone.com g++ C++14.
I am using Ideone.com g++ C++14 with -O2 option.
推荐答案
这是由于未定义的行为,您在循环的最后一次迭代中访问了超出范围的数组 mc
.一些编译器可能会围绕没有未定义行为的假设执行积极的循环优化.逻辑类似于以下内容:
This is due to undefined behavior, you are accessing the array mc
out of bounds on the last iteration of your loop. Some compilers may perform aggressive loop optimization around the assumptions of no undefined behavior. The logic would be similar to the following:
- 越界访问
mc
是未定义的行为 - 假设没有未定义的行为
- 因此
di <4
始终为真,否则mc[di]
将调用未定义的行为
- Accessing
mc
out of bounds is undefined behavior - Assume no undefined behavior
- Therefore
di < 4
is always true since otherwisemc[di]
would invoke undefined behavior
打开优化并使用 -fno-aggressive-loop-optimizations
标志的 gcc 会导致无限循环行为消失(现场观看).虽然 有优化但没有 -fno-aggressive-loop-optimizations 的实例表现出无限循环行为你观察.
gcc with optimization turned on and using the -fno-aggressive-loop-optimizations
flag causes the infinite loop behavior to disappear(see it live). While a live example with optimization but without -fno-aggressive-loop-optimizations exhibits the infinite loop behavior you observe.
A godbolt 实例代码的显示了di <4
检查被移除并替换为无条件jmp:
A godbolt live example of the code shows the di < 4
check is removed and replaced with and unconditional jmp:
jmp .L6
这几乎与 GCC pre-4.8 Breaks Broken SPEC 2006 Benchmarks 中概述的情况相同.这篇文章的评论非常好,非常值得一读.它指出,clang 使用 -fsanitize=undefined
捕获了文章中的案例,我无法为此案例重现,但使用 -fsanitize=undefined
的 gcc 可以(现场观看).围绕未定义行为进行推断的优化器最臭名昭著的错误可能是 Linux 内核空指针检查删除.
This is almost identical to the case outlined in GCC pre-4.8 Breaks Broken SPEC 2006 Benchmarks. The comments to this article are excellent and well worth the read. It notes that clang caught the case in the article using -fsanitize=undefined
which I can not reproduce for this case but gcc using -fsanitize=undefined
does (see it live). Probably the most infamous bug around an optimizer making an inference around undefined behavior is the Linux kernel null pointer check removal.
尽管这是一种激进的优化,但重要的是要注意,正如 C++ 标准所说的未定义行为:
Although this is an aggressive optimizations, it is important to note that as the C++ standard says undefined behavior is:
本国际标准没有要求的行为
behavior for which this International Standard imposes no requirements
这基本上意味着一切皆有可能,并指出(强调我的):
Which essentially means anything is possible and it notes (emphasis mine):
[...]允许的未定义行为范围从完全无视情况导致不可预测的结果,到在翻译或以环境特征的文件化方式执行程序(有或没有发布诊断消息),终止翻译或执行(伴随诊断消息的发布).[...]
[...]Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).[...]
为了从 gcc 获得警告,我们需要将 cout
移到循环之外,然后我们会看到以下警告(现场观看):
In order to get a warning from gcc we need to move the cout
outside the loop and then we see the following warning (see it live):
warning: iteration 3u invokes undefined behavior [-Waggressive-loop-optimizations]
for(di=0; di<4;di++,delta=mc[di]){ }
^
这可能足以为 OP 提供足够的信息来弄清楚发生了什么.像这样的不一致是我们可以看到的未定义行为的典型行为类型.为了更好地理解为什么这种警告在面对未定义的行为时会不一致 在基于未定义行为进行优化时为什么不能发出警告? 是个好读物.
which would have likely been sufficient to provide the OP with enough information to figure out what was going on. Inconsistency like this are typical of the types of behavior we can see with undefined behavior. To get a better understanding of why such waring can be inconsitent in the face of undefined behavior Why can't you warn when optimizing based on undefined behavior? is a good read.
注意,-fno-aggressive-loop-optimizations
记录在 gcc 4.8 发行说明.
相关文章