#include <stdio.h>
#include <limits.h>
void sanity_check(int x)
{
if (x < 0)
{
x = -x;
}
if (x == INT_MIN)
{
printf("%d == %d\n", x, INT_MIN);
}
else
{
printf("%d != %d\n", x, INT_MIN);
}
if (x < 0)
{
printf("negative number: %d\n", x);
}
else
{
printf("positive number: %d\n", x);
}
}
int main(void)
{
sanity_check(42);
sanity_check(-97);
sanity_check(INT_MIN);
return 0;
}
当我使用gcc wtf.c
编译上述程序时,我得到了预期的输出:
42 != -2147483648
positive number: 42
97 != -2147483648
positive number: 97
-2147483648 == -2147483648
negative number: -2147483648
然而,当我使用gcc -O2 wtf.c
编译程序时,我得到了不同的输出:
42 != -2147483648
positive number: 42
97 != -2147483648
positive number: 97
-2147483648 != -2147483648
positive number: -2147483648
注意最后两行。这到底是怎么回事? gcc 4.6.3是否过于热切地优化了?
(我也用g ++ 4.6.3对此进行了测试,我发现了同样奇怪的行为,因此也就是C ++标签。)
答案 0 :(得分:15)
执行 - (INT_MIN)时,您正在调用未定义的行为,因为该结果不适合int。
gcc -O2注意到x永远不会是负数并且之后会优化。你并不关心你是否已超出价值,因为它未定义,它可以随心所欲地对待它。
答案 1 :(得分:12)
我认为这可以帮到你,来自这里:here
-fstrict溢出 允许编译器采用严格的签名溢出规则,具体取决于正在编译的语言。对于C(和C ++),这意味着在对带符号数进行算术运算时溢出是未定义的,这意味着编译器可能会认为它不会发生。这允许各种优化。例如,编译器将假设类似于i + 10&gt;的表达式。我将永远忠于签名我。只有在未定义有符号溢出时,此假设才有效,因为如果使用二进制补码运算时i + 10溢出,则表达式为false。当此选项生效时,必须小心写入任何确定对有符号数字的操作是否溢出的尝试,以免实际涉及溢出。 此选项还允许编译器采用严格的指针语义:给定指向对象的指针,如果向该指针添加偏移量不会产生指向同一对象的指针,则添加是未定义的。这允许编译器得出结论p + u>对于指针p和无符号整数u,p始终为true。这个假设只是有效,因为指针环绕是未定义的,因为如果p + u使用二进制补码算术溢出,则表达式为false。
另请参阅-fwrapv选项。使用-fwrapv意味着完全定义了整数签名溢出:它包装。使用-fwrapv时,-fstrict-overflow和-fno-strict-overflow对整数没有区别。使用-fwrapv允许某些类型的溢出。例如,如果编译器在对常量进行算术运算时出现溢出,则溢出的值仍然可以与-fwrapv一起使用,但不能与其他情况一起使用。
在-O2,-O3,-Os级别启用-fstrict-overflow选项。