#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;
}
When I compile the above program with gcc wtf.c
, I get the expected output:
当我用gcc wtf.c编译上面的程序时,我得到了预期的输出:
42 != -2147483648
positive number: 42
97 != -2147483648
positive number: 97
-2147483648 == -2147483648
negative number: -2147483648
However, when I compile the program with gcc -O2 wtf.c
, I get a different output:
但是,当我用gcc -O2 wtf.c编译程序时,我得到一个不同的输出:
42 != -2147483648
positive number: 42
97 != -2147483648
positive number: 97
-2147483648 != -2147483648
positive number: -2147483648
Note the last two lines. What on earth is going on here? Is gcc 4.6.3 optimizing a bit too eagerly?
注意最后两行。到底是怎么回事? gcc 4.6.3是否过于热切地优化了?
(I also tested this with g++ 4.6.3, and I observed the same strange behavior, hence the C++ tag.)
(我也用g ++ 4.6.3对它进行了测试,我发现了同样奇怪的行为,因此也就是C ++标签。)
2 个解决方案
#1
15
When you do -(INT_MIN) you're invoking undefined behavior, since that result can't fit in an int.
当你执行 - (INT_MIN)时,你正在调用未定义的行为,因为该结果不适合int。
gcc -O2 notices that x can never be negative and optimizes thereafter. It doesn't care that you overflowed the value since that's undefined and it can treat it however it wants.
gcc -O2注意到x永远不会是负数并且之后会优化。它并不关心你是否溢出了这个值,因为它是未定义的,它可以随心所欲地对待它。
#2
12
I think this could help you, is from here :here
我想这可以帮助你,来自这里:这里
-fstrict-overflow Allow the compiler to assume strict signed overflow rules, depending on the language being compiled. For C (and C++) this means that overflow when doing arithmetic with signed numbers is undefined, which means that the compiler may assume that it will not happen. This permits various optimizations. For example, the compiler will assume that an expression like i + 10 > i will always be true for signed i. This assumption is only valid if signed overflow is undefined, as the expression is false if i + 10 overflows when using twos complement arithmetic. When this option is in effect any attempt to determine whether an operation on signed numbers will overflow must be written carefully to not actually involve overflow. This option also allows the compiler to assume strict pointer semantics: given a pointer to an object, if adding an offset to that pointer does not produce a pointer to the same object, the addition is undefined. This permits the compiler to conclude that p + u > p is always true for a pointer p and unsigned integer u. This assumption is only valid because pointer wraparound is undefined, as the expression is false if p + u overflows using twos complement arithmetic.
-fstrict-overflow允许编译器采用严格的签名溢出规则,具体取决于编译的语言。对于C(和C ++),这意味着在对带符号数进行算术运算时溢出是未定义的,这意味着编译器可能会认为它不会发生。这允许各种优化。例如,编译器将假设类似i + 10> i的表达式对于signed i将始终为true。只有在未定义有符号溢出时,此假设才有效,因为如果在使用二进制补码算法时i + 10溢出,则表达式为false。当此选项生效时,必须仔细写入任何确定对有符号数字的操作是否会溢出的尝试,而不是实际涉及溢出。此选项还允许编译器采用严格的指针语义:给定指向对象的指针,如果向该指针添加偏移量不会产生指向同一对象的指针,则添加是未定义的。这允许编译器得出结论:对于指针p和无符号整数u,p + u> p总是为真。此假设仅有效,因为指针环绕是未定义的,因为如果p + u使用二进制补码算术溢出,则表达式为false。
See also the -fwrapv option. Using -fwrapv means that integer signed overflow is fully defined: it wraps. When -fwrapv is used, there is no difference between -fstrict-overflow and -fno-strict-overflow for integers. With -fwrapv certain types of overflow are permitted. For example, if the compiler gets an overflow when doing arithmetic on constants, the overflowed value can still be used with -fwrapv, but not otherwise.
另请参见-fwrapv选项。使用-fwrapv意味着完全定义了整数签名溢出:它包装。使用-fwrapv时,-fstrict-overflow和-fno-strict-overflow对整数没有区别。使用-fwrapv允许某些类型的溢出。例如,如果编译器在对常量进行算术运算时出现溢出,则溢出的值仍可与-fwrapv一起使用,但不能与其他情况一起使用。
The -fstrict-overflow option is enabled at levels -O2, -O3, -Os.
-fstrict-overflow选项在-O2,-O3,-Os级别启用。
#1
15
When you do -(INT_MIN) you're invoking undefined behavior, since that result can't fit in an int.
当你执行 - (INT_MIN)时,你正在调用未定义的行为,因为该结果不适合int。
gcc -O2 notices that x can never be negative and optimizes thereafter. It doesn't care that you overflowed the value since that's undefined and it can treat it however it wants.
gcc -O2注意到x永远不会是负数并且之后会优化。它并不关心你是否溢出了这个值,因为它是未定义的,它可以随心所欲地对待它。
#2
12
I think this could help you, is from here :here
我想这可以帮助你,来自这里:这里
-fstrict-overflow Allow the compiler to assume strict signed overflow rules, depending on the language being compiled. For C (and C++) this means that overflow when doing arithmetic with signed numbers is undefined, which means that the compiler may assume that it will not happen. This permits various optimizations. For example, the compiler will assume that an expression like i + 10 > i will always be true for signed i. This assumption is only valid if signed overflow is undefined, as the expression is false if i + 10 overflows when using twos complement arithmetic. When this option is in effect any attempt to determine whether an operation on signed numbers will overflow must be written carefully to not actually involve overflow. This option also allows the compiler to assume strict pointer semantics: given a pointer to an object, if adding an offset to that pointer does not produce a pointer to the same object, the addition is undefined. This permits the compiler to conclude that p + u > p is always true for a pointer p and unsigned integer u. This assumption is only valid because pointer wraparound is undefined, as the expression is false if p + u overflows using twos complement arithmetic.
-fstrict-overflow允许编译器采用严格的签名溢出规则,具体取决于编译的语言。对于C(和C ++),这意味着在对带符号数进行算术运算时溢出是未定义的,这意味着编译器可能会认为它不会发生。这允许各种优化。例如,编译器将假设类似i + 10> i的表达式对于signed i将始终为true。只有在未定义有符号溢出时,此假设才有效,因为如果在使用二进制补码算法时i + 10溢出,则表达式为false。当此选项生效时,必须仔细写入任何确定对有符号数字的操作是否会溢出的尝试,而不是实际涉及溢出。此选项还允许编译器采用严格的指针语义:给定指向对象的指针,如果向该指针添加偏移量不会产生指向同一对象的指针,则添加是未定义的。这允许编译器得出结论:对于指针p和无符号整数u,p + u> p总是为真。此假设仅有效,因为指针环绕是未定义的,因为如果p + u使用二进制补码算术溢出,则表达式为false。
See also the -fwrapv option. Using -fwrapv means that integer signed overflow is fully defined: it wraps. When -fwrapv is used, there is no difference between -fstrict-overflow and -fno-strict-overflow for integers. With -fwrapv certain types of overflow are permitted. For example, if the compiler gets an overflow when doing arithmetic on constants, the overflowed value can still be used with -fwrapv, but not otherwise.
另请参见-fwrapv选项。使用-fwrapv意味着完全定义了整数签名溢出:它包装。使用-fwrapv时,-fstrict-overflow和-fno-strict-overflow对整数没有区别。使用-fwrapv允许某些类型的溢出。例如,如果编译器在对常量进行算术运算时出现溢出,则溢出的值仍可与-fwrapv一起使用,但不能与其他情况一起使用。
The -fstrict-overflow option is enabled at levels -O2, -O3, -Os.
-fstrict-overflow选项在-O2,-O3,-Os级别启用。