【问题标题】:Can someone explain to me how this is undefined behavior?有人可以向我解释这是未定义的行为吗?
【发布时间】: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 &lt; 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


【解决方案1】:

这似乎与 gcc 中的 a known bug 重复,与 -Waggressive-loop-optimizations 有关。但是鉴于您的示例比我在duplicates 中看到的那个错误的示例要简单得多,我建议您将该示例报告给已确认的错误报告(参见链接)。

有趣的是,这个问题似乎只存在于-O2,而不存在于现有报告中的-O1-O3

【讨论】:

  • 该链接的评论 #3 中的复制看起来相当简单。
  • @BenVoigt 哦,是的,您可以认为该示例更小,但更简单,我不确定。我们在这里有一个大小可控的数组,这一事实让我觉得它更令人困惑。注释 #3 没有指定数组的来源。无论如何,额外的例子可能并没有什么坏处。
  • @bitmask 随意添加此示例。我不能,因为帐户创建受到限制(对于 bugzilla)
猜你喜欢
  • 1970-01-01
  • 2022-12-16
  • 2022-01-22
  • 1970-01-01
  • 2011-08-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多