看似不相关的变量的 g++ "warning: iteration ... invokes undefined behavior"

g++ "warning: iteration ... invokes undefined behavior" for Seemingly Unrelated Variable

本文关键字:iteration invokes undefined behavior warning 变量 g++ 不相关      更新时间:2023-10-16

考虑以下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) // (*)
         ^

我不明白为什么增量会调用未定义的行为。此外,还有两个更改,每个更改都会使警告消失:

  1. 将行(*)更改为for(int j...
  2. 将行(**)更改为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 并溢出。

  1. 通过将循环的控制条件转换为同义重复为真表达式来"修复"它;这使得循环成为无限循环,因为循环内的if永远不会执行,因为&i不能是空指针。无限循环可以优化,因此 gcc 消除了循环的主体,并且不会发生i的整数溢出。

  2. 通过允许 GCC 从整数溢出的未定义行为中"修复"它。防止整数溢出的唯一方法是i的初始值为负,以便在某个时候i达到零。这是可能的(见上文(,唯一的选择是未定义的行为,因此必须发生。因此,i达到零,循环内的if执行,main返回-1