程序在在线IDE上表现奇怪



我遇到了以下C 程序(来源(:

#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而不是其他值?

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

-faggressive-loop-optimizations

此选项告诉循环优化器使用语言约束来得出循环迭代次数的界限。这是假设循环代码不会通过例如引起签名的整数溢出或界外数组访问来调用未定义的行为。循环迭代次数的边界用于指导循环展开,剥离和循环退出测试优化。默认情况下启用此选项。

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


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

为什么4169而不是其他值?

这些站点可能会限制它们显示并恰好共享相同限制的输出线的数量(或字符或字节(。

"未定义的行为是未定义的。" (c(

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

  1. 不确定的行为不会发生。
  2. i * 12345678如果i > 173(假设32位int s(。
  3. 因此,i永远无法超过173
  4. 因此,i < 300是多余的,可以用true代替。

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

您正在调用for循环内部第174次迭代的未定义行为,因为最大int值可能是2147483647,但174 * 123456789表达式评估对2148147972,这是不确定的行为,该行为没有签名的Integer溢出。因此,您正在观察UB的效果,尤其是使用GCC编译器,并在您的情况下设置了优化标志。编译器可能会通过发出以下警告来警告您:

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

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

编译器可以假定不会发生未定义的行为,并且由于签名的溢出为UB,因此可以假设从不i * 12345678 > INT_MAX,因此也可以i <= INT_MAX / 12345678 < 300,因此也可以删除检查i < 300

最新更新