gcc -O2的奇怪整数行为

时间:2012-10-04 14:06:59

标签: 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;
}

当我使用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 ++标签。)

2 个答案:

答案 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选项。