看似不相关的变量的 g++ "warning: iteration ... invokes undefined behavior"
g++ "warning: iteration ... invokes undefined behavior" for Seemingly Unrelated Variable
考虑以下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
增量
是未定义的,因为一旦i
达到std::numeric_limits<int>::max()
(在 32 位、LP64 或 LLP64 平台上为 231 - 1(,递增它将溢出,这是有符号整数类型的未定义行为。
GCC 在迭代 4294967296ul (232( 上发出警告,而不是像您期望的那样在迭代 2147483646U (231( 上发出警告,因为它不知道 i
的初始值;在main
之前,其他一些代码可能已经运行以将i
设置为 0
以外的内容。但是一旦输入main
,就没有其他代码可以运行来更改i
,因此一旦 232 次迭代完成,它将在某个时候达到 231 - 1 并溢出。
通过将循环的控制条件转换为同义重复为真表达式来"修复"它;这使得循环成为无限循环,因为循环内的
if
永远不会执行,因为&i
不能是空指针。无限循环可以优化,因此 gcc 消除了循环的主体,并且不会发生i
的整数溢出。通过允许 GCC 从整数溢出的未定义行为中"修复"它。防止整数溢出的唯一方法是
i
的初始值为负,以便在某个时候i
达到零。这是可能的(见上文(,唯一的选择是未定义的行为,因此必须发生。因此,i
达到零,循环内的if
执行,main
返回-1
。