c++编译错误
C++ compilation bug?
我有以下代码:
#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,....."
看起来比较di<4
不太好,总是返回true。
如果我只是注释掉,delta=mc[di]
,我得到"0,1,2,3"正常。无辜的任务有什么问题吗?
我使用Ideone.com g++ c++ 14与-O2选项。
这是由于未定义的行为,您在循环的最后一次迭代中访问数组mc
越界。一些编译器可能会围绕没有未定义行为的假设执行积极的循环优化。逻辑类似如下:
- 越界访问
mc
是未定义行为 - 假定没有未定义行为
- 因此
di < 4
总是为真,否则mc[di]
将调用未定义行为
gcc与优化打开和使用-fno-aggressive-loop-optimizations
标志导致无限循环行为消失(看到它的实时)。而一个有优化但没有-fno-aggressive-loop-优化的实例显示了您所观察到的无限循环行为。
代码的一个实时示例显示,di < 4
检查被删除并替换为无条件的jmp:
jmp .L6
这几乎与GCC 4.8之前的破损SPEC 2006基准中概述的情况相同。对这篇文章的评论非常好,非常值得一读。它注意到clang在文章中使用-fsanitize=undefined
捕获了这种情况,我无法为这种情况复制这种情况,但gcc使用-fsanitize=undefined
可以(查看现场)。关于优化器对未定义行为进行推断的最臭名昭著的错误可能是Linux内核空指针检查删除。
虽然这是一个激进的优化,但重要的是要注意,正如c++标准所说,未定义的行为是:
本国际标准未规定的行为
本质上意味着一切皆有可能,它注意到(强调mine):
[…允许的未定义行为范围从完全忽略情况导致不可预测的结果,到在翻译或翻译过程中的行为以环境特征的文档化方式执行程序(有或没有发出(诊断消息),以终止翻译或执行(通过发出诊断消息)。[…]
为了从gcc获得警告,我们需要将cout
移出循环,然后我们看到以下警告(see it live):
warning: iteration 3u invokes undefined behavior [-Waggressive-loop-optimizations]
for(di=0; di<4;di++,delta=mc[di]){ }
^
可能足以为OP提供足够的信息来弄清楚发生了什么。像这样的不一致是我们在未定义行为中看到的典型行为类型。为了更好地理解为什么这种警告在面对未定义行为时可能不一致,为什么不能在基于未定义行为进行优化时发出警告?是一本好书。
注意,-fno-aggressive-loop-optimizations
在gcc 4.8发行说明中有文档记录。
由于您在使用di
索引mc
之前对其进行了递增,因此在循环的第四次中您将引用mc[4],它超出了数组的末尾,这可能会导致麻烦的行为。
你有这个:
for(int di=0; di<4; di++, delta=mc[di]) {
cout<<di<<endl;
}
试试这个:
for(int di=0; di<4; delta=mc[di++]) {
cout<<di<<endl;
}
编辑:为了弄清发生了什么,让我们分解For循环的迭代:
第一次迭代:最初di被设置为0。比较检查:di是否小于4?好的,继续。将di增加1。现在di = 1。抓取mc[]的第n个元素并将其设置为delta。这次我们抓取的是第二个元素,因为这个索引值是1而不是0。最后在for循环内执行代码块/s。
第二次迭代:现在di被设置为1。比较检查:di是否小于4?是的,继续。将di增加1。现在di = 2。抓取mc[]的第n个元素并将其设置为delta。这次我们抓取第三个元素,因为这个索引值是2。最后在for循环内执行代码块/s。
第三次迭代:现在di被设置为2。比较检查:di是否小于4?是的,继续。将di增加1。现在di = 3。抓取mc[]的第n个元素并将其设置为delta。这次我们抓取第4个元素,因为这个索引值是3。最后在for循环内执行代码块/s。
第四次迭代:现在di被设置为3。比较检查:di是否小于4?是的,继续。将di增加1。现在di = 4。(你知道这是怎么回事吗?)抓取mc[]的第n个元素并将其设置为delta。这次我们抓取第5个元素,因为这个索引值是4。哦,我们有麻烦了;我们的数组大小只有4。Delta现在有垃圾,这是未定义的行为或损坏。最后,在for循环中使用"垃圾增量"执行代码块。
第五迭代。现在di被设置为4。比较检查:di是否小于4?不,跳出循环
由于超出连续内存(数组)的边界而损坏。
这是因为di++是在循环的最后运行时执行的。
例如,
int di = 0;
for(; di < 4; di++);
// after the loop di == 4
// (inside the loop we see 0,1,2,3)
// (inside the for statement, after di++, we see 1,2,3,4)
当di == 4时,你正在访问mc[],所以这是一个越界问题,可能会破坏部分堆栈并损坏变量di。
一个解决方案是:
for(int di = 0; di < 4; di++) {
cout << di << endl;
delta = mc[di];
}
- 用MacOS Mojave编译C++:致命错误:mpi.h:没有这样的文件或目录
- std::is_base_of表示ctor编译错误
- 我的项目不会像"undefined reference to `grpc::g_core_codegen_interface'"那样使用未定义的引用错误进行编译
- 在没有定义返回类型的函数中返回布尔值,并将结果保存在无错误的char编译中-为什么
- Qt5:使用QCommandLineParser类时出现奇怪的编译错误
- Qt Cmake 错误编译"GuiSupportQt not found"
- Opengl 精度转换错误编译错误 E0415
- 库将ARM架构错误编译为架构X64
- RT 音频 Mac 错误 g++ 编译错误
- 错误编译Boost.log
- 错误编译QT创建者 / QT窗口小部件示例
- 错误编译MIPS32
- Visual Studio 2013 中的错误(编译和运行代码)
- 链接错误编译qt项目在visual 2010
- 无法用模板错误编译nsgmls
- 奇怪的错误.编译失败
- 如果有人调用c++中的方法,则强制错误(编译时)
- 来自autoconf测试的错误编译命令
- 时间限制超出错误C++编译
- SFML 2.3 和 CodeBlocks 错误编译