arp*_*gal 78 c++ integer-overflow undefined-behavior
我遇到了下面的C++程序(源代码):
#include <iostream>
int main()
{
for (int i = 0; i < 300; i++)
std::cout << i << " " << i * 12345678 << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
它看起来像一个简单的程序,并在我的本地机器上提供正确的输出,例如:
0 0
1 12345678
2 24691356
...
297 -628300930
298 -615955252
299 -603609574
Run Code Online (Sandbox Code Playgroud)
但是,在像codechef这样的在线IDE上,它提供了以下输出:
0 0
1 12345678
2 24691356
...
4167 -95167326
4168 -82821648
4169 -7047597
Run Code Online (Sandbox Code Playgroud)
为什么for
循环不在300处终止?此程序也始终终止4169
.为什么4169
而不是其他一些价值?
eer*_*ika 103
我将假设在线编译器使用GCC或兼容的编译器.当然,也允许任何其他编译器进行相同的优化,但GCC文档很好地解释了它的作用:
-faggressive-loop-optimizations
此选项告诉循环优化器使用语言约束来导出循环迭代次数的边界.这假设循环代码不会通过例如导致有符号整数溢出或超出范围的数组访问来调用未定义的行为.循环迭代次数的界限用于指导循环展开和剥离以及循环退出测试优化.默认情况下启用此选项.
该选项仅允许基于证明UB的情况做出假设.为了利用这些假设,可能需要启用其他优化,例如常量折叠.
有符号整数溢出具有未定义的行为.优化器能够证明任何i
大于173的值都会导致UB,并且因为它可以假设没有UB,它也可以假设它i
永远不会大于173.它可以进一步证明i < 300
总是如此,并且所以可以优化循环条件.
为什么4169而不是其他一些价值?
这些站点可能会限制它们显示的输出行(或字符或字节)的数量,并且碰巧共享相同的限制.
Hol*_*Cat 35
"未定义的行为是未定义的." (C)
codechef上使用的编译器似乎使用以下逻辑:
i * 12345678
如果i > 173
(假设32位int
s)溢出并导致UB .i
永远不会超过173
.i < 300
是多余的,可以替换为true
.循环本身似乎是无限的.显然,codechef会在特定时间后停止程序或截断输出.
您正在调用未定义行为的第174次迭代可能是你的内for
环路最高int
值可能是2147483647
尚未174 * 123456789
表达式求2148147972
是未定义的行为,因为没有符号的整数溢出.因此,您正在观察UB的影响,特别是在GCC编译器中设置了优化标志.编译器可能会通过发出以下警告警告您:
warning: iteration 174 invokes undefined behavior [-Waggressive-loop-optimizations]
Run Code Online (Sandbox Code Playgroud)
删除(-O2
)优化标志以观察不同的结果.
编译器可以假设不会发生未定义的行为,并且因为签名溢出是UB,它可以假设从不i * 12345678 > INT_MAX
,因此也i <= INT_MAX / 12345678 < 300
因此删除了检查i < 300
.