请考虑strange.cpp
中的以下代码:
#include <vector>
using namespace std;
int i = 0;
int *bar()
{
++i;
return &i;
}
int main()
{
for(size_t j = 0; j < 99999999999; ++j) // (*)
{
const auto p = bar();
if(!p) // (**)
return -1;
}
}
用g ++编译它会发出警告:
$ g++ --std=c++11 -O3 strange.cpp
strange.cpp: In function ‘int main()’:
strange.cpp:12:12: warning: iteration 4294967296ul invokes undefined behavior [-Waggressive-loop-optimizations]
++i;
^
strange.cpp:19:9: note: containing loop
for(size_t j = 0; j < 99999999999; ++j) // (*)
^
我不明白为什么增量会调用未定义的行为。此外,还有两个变化,每个变化都会使警告消失:
(*)
更改为for(int j...
(**)
更改为if(!*p)
此警告的含义是什么,为什么更改与之相关?
注意
$ g++ --version
g++ (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4
答案 0 :(得分:3)
增量未定义,因为一旦i
到达std::numeric_limits<int>::max()
(2 31 - 在32位,LP64或LLP64平台上为1),递增它将溢出,是有符号整数类型的未定义行为。
gcc在迭代4294967296ul(2 32 )而不是迭代2147483646u(2 31 )时发出警告,因为它不知道初始值i
;其他一些代码可能在main
之前运行,以将i
设置为0
以外的其他代码。但是一旦输入main
,就没有其他代码可以运行来改变i
,所以一旦2 32 迭代完成,它将在某个时刻达到2 31 - 1并且溢出。
通过将循环的控制条件转换为同义的真实表达来“修复”它;这使得循环成为无限循环,因为循环内的if
将永远不会执行,因为&i
不能是空指针。 Infinite loops can be optimized away,所以gcc消除了循环体,并且i
的整数溢出不会发生。
通过允许gcc从整数溢出的未定义行为中取出来“修复”它。防止整数溢出的唯一方法是i
具有负值的初始值,以便在某个点i
达到零。这是可能的(见上文),唯一的选择是未定义的行为,因此必须发生。因此i
达到零,循环内的if
执行,main
返回-1
。