对于看似无关的变量,g ++“warning:iteration ...调用未定义的行为”

时间:2016-02-09 18:05:35

标签: c++ g++ compiler-warnings

请考虑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)
  3. 此警告的含义是什么,为什么更改与之相关?

    注意

    $ g++ --version
    g++ (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4
    

1 个答案:

答案 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并且溢出。

  1. 通过将循环的控制条件转换为同义的真实表达来“修复”它;这使得循环成为无限循环,因为循环内的if将永远不会执行,因为&i不能是空指针。 Infinite loops can be optimized away,所以gcc消除了循环体,并且i的整数溢出不会发生。

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