gcc -O2的奇怪整数行为

fre*_*low 9 c c++ gcc integer compiler-optimization

#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;
}
Run Code Online (Sandbox Code Playgroud)

当我用上面的程序编译时gcc wtf.c,我得到了预期的输出:

42 != -2147483648
positive number: 42
97 != -2147483648
positive number: 97
-2147483648 == -2147483648
negative number: -2147483648
Run Code Online (Sandbox Code Playgroud)

但是,当我编译程序时gcc -O2 wtf.c,我得到一个不同的输出:

42 != -2147483648
positive number: 42
97 != -2147483648
positive number: 97
-2147483648 != -2147483648
positive number: -2147483648
Run Code Online (Sandbox Code Playgroud)

注意最后两行.这到底是怎么回事?gcc 4.6.3是否过于热切地优化了?

(我也用g ++ 4.6.3对它进行了测试,我发现了同样奇怪的行为,因此也就是C++标签.)

Per*_*son 15

当你执行 - (INT_MIN)时,你正在调用未定义的行为,因为该结果不适合int.

gcc -O2注意到x永远不会是负数并且之后会优化.它并不关心你是否溢出了这个值,因为它是未定义的,它可以随心所欲地对待它.

  • 在pedr0的答案中有一个例子.更多信息请参见此处:http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html#signed_overflow (2认同)

ped*_*dr0 12

我想这可以帮到你,就是来自这里:这里

-fstrict-overflow允许编译器采用严格的签名溢出规则,具体取决于编译的语言.对于C(和C++),这意味着在对带符号数进行算术运算时溢出是未定义的,这意味着编译器可能会认为它不会发生.这允许各种优化.例如,编译器将假设类似i + 10> i的表达式对于signed i将始终为true.只有在未定义有符号溢出时,此假设才有效,因为如果使用二进制补码运算时i + 10溢出,则表达式为false.当此选项生效时,必须小心写入任何确定对有符号数字的操作是否溢出的尝试,以免实际涉及溢出.此选项还允许编译器采用严格的指针语义:给定指向对象的指针,如果向该指针添加偏移量不会产生指向同一对象的指针,则添加是未定义的.这允许编译器得出结论:对于指针p和无符号整数u,p + u> p总是为真.此假设仅有效,因为指针环绕是未定义的,因为如果p + u使用二进制补码算术溢出,则表达式为false.

另请参见-fwrapv选项.使用-fwrapv意味着完全定义了整数签名溢出:它包装.使用-fwrapv时,-fstrict-overflow和-fno-strict-overflow对整数没有区别.使用-fwrapv允许某些类型的溢出.例如,如果编译器在对常量进行算术运算时出现溢出,则溢出的值仍然可以与-fwrapv一起使用,但不能与其他情况一起使用.

-fstrict-overflow选项在-O2,-O3,-Os级别启用.