【发布时间】:2021-08-23 22:15:35
【问题描述】:
我有一个用 -O2 编译的简单程序。
编译器版本:g++ (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
#include <cstdlib>
constexpr size_t NUM_ELEMENTS = 10000;
void init(float *a) {
a[0] = 42;
size_t i;
for (i = 1; i < NUM_ELEMENTS - 2; i += 3) {
a[i] = a[i+1] = a[i+2] = 42;
}
while (i < NUM_ELEMENTS) {
a[i] = 42;
i++;
}
}
int main() {
float a[NUM_ELEMENTS];
init(a);
return 0;
}
而 g++ 给了我这个(我无法解释的)警告
foo.cpp: In function ‘void init(float*)’:
foo.cpp:14:12: warning: iteration 4611686018427387903 invokes undefined behavior [-Waggressive-loop-optimizations]
14 | a[i] = 42;
| ^
foo.cpp:13:14: note: within this loop
13 | while (i < NUM_ELEMENTS) {
| ~~^~~~~~~~~~~~~~
如果我用等效的 for (; i < NUM_ELEMENTS; i++) 更改 while,警告就会消失。如果我将数组更改为全局并且我只是随机访问它而不将它作为参数传递,警告就会消失。
我读过我理解的类似 SO 问题,但这让我感到困惑。如果我不得不猜测,我会说 g++ 知道 while 循环永远不会运行(因为 i == 10000 在第一个循环结束时)并且它会进行某种导致此警告的激进转换?救命!
【问题讨论】:
-
@Eljay:
for循环?while循环? -
@OP:我也不知所措:您总是在为数组下标之前进行边界检查。由于提升规则,
NUM_ELEMENTS-2使用无符号算术执行并且可以回绕...但当NUM_ELEMENTS为 10000 时不会。编译器实际上应该从-funroll-loops自动生成这种模式。 -
@Eljay 这段代码本质上是我从“程序员的视角”中获取的循环展开程序的简化。我 99.9% 确定索引检查是正确的。但是,我确实将断言添加到循环中,但没有看到中止。
-
@BenVoigt 是的,如果我将步骤更改为 +2 并相应地调整索引,以便 while 循环再次不会运行,我不会收到警告。真是莫名其妙。
-
你用的是什么版本的g++?
标签: c++ g++ undefined-behavior compiler-bug