程序在在线 IDE 上表现异常

Program behaving strangely on online IDEs

我遇到了下面的 C++ 程序 (source):

#include <iostream>
int main()
{
    for (int i = 0; i < 300; i++)
        std::cout << i << " " << i * 12345678 << std::endl;
}

它看起来像一个简单的程序,并在我的本地机器上给出了正确的输出,例如:

0 0
1 12345678
2 24691356
...
297 -628300930
298 -615955252
299 -603609574

但是,在像 codechef 这样的在线 IDE 上,它会给出以下输出:

0 0
1 12345678
2 24691356
...
4167 -95167326
4168 -82821648
4169 -7047597

为什么 for 循环不在 300 处终止?此外,该程序总是在 4169 上终止。为什么 4169 而不是其他值?

您可能在 for 循环中的第 174 次迭代中调用 undefined behavior,因为最大 int 值可能是 2147483647,但 174 * 123456789 表达式的计算结果为2148147972 这是未定义的行为,因为没有有符号整数溢出。因此,您正在观察 UB 的效果,特别是在您的案例中设置了优化标志的 GCC 编译器。编译器可能会通过发出以下警告来警告您:

warning: iteration 174 invokes undefined behavior [-Waggressive-loop-optimizations]

删除 (-O2) 优化标志以观察不同的结果。

"Undefined behaviour is undefined." (c)

在 codechef 上使用的编译器似乎使用以下逻辑:

  1. 不能发生未定义的行为。
  2. 如果 i > 173(假设 32 位 ints),
  3. i * 12345678 溢出并导致 UB。
  4. 因此,i 永远不会超过 173
  5. 因此i < 300是多余的,可以用true代替。

循环本身似乎是无限的。显然,codechef 只​​是在特定时间后停止程序或截断输出。

编译器可以假定不会发生未定义的行为,并且因为有符号溢出是 UB,它可以假定永远不会 i * 12345678 > INT_MAX,因此也 i <= INT_MAX / 12345678 < 300 因此删除检查 i < 300.

我假设在线编译器使用 GCC 或兼容的编译器。当然,也允许任何其他编译器进行相同的优化,但 GCC 文档很好地解释了它的作用:

-faggressive-loop-optimizations

This option tells the loop optimizer to use language constraints to derive bounds for the number of iterations of a loop. This assumes that loop code does not invoke undefined behavior by for example causing signed integer overflows or out-of-bound array accesses. The bounds for the number of iterations of a loop are used to guide loop unrolling and peeling and loop exit test optimizations. This option is enabled by default.

此选项仅允许根据已证明 UB 的情况进行假设。要利用这些假设,可能需要启用其他优化,例如常量折叠。


有符号整数溢出具有未定义的行为。优化器能够证明 i 的任何值大于 173 都会导致 UB,并且因为它可以假设没有 UB,所以它也可以假设 i 永远不会大于 173。它然后可以进一步证明 i < 300 始终为真,因此循环条件可以被优化掉。

Why 4169 and not some other value?

这些站点可能限制了它们显示的输出行(或字符或字节)的数量,并且碰巧共享相同的限制。