9
#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

最後の 2 行に注意してください。ここで何が起こっているのですか?gcc 4.6.3 の最適化は少し熱心すぎますか?

(これも g++ 4.6.3 でテストしましたが、同じ奇妙な動作が見られたため、C++ タグが付けられました。)

4

2 に答える 2

15

-(INT_MIN) を実行すると、結果が int に収まらないため、未定義の動作が呼び出されます。

gcc -O2 は、x が負になることは決してないことに気づき、その後最適化します。値が未定義であるため、値がオーバーフローしても気にしません。必要に応じて処理できます。

于 2012-10-04T14:11:49.457 に答える
12

これはあなたを助けることができると思います、ここからです:ここから

-fstrict-overflow コンパイルされる言語に応じて、コンパイラが厳密な符号付きオーバーフロー ルールを想定できるようにします。C (および C++) の場合、これは、符号付き数値で算術演算を行うときのオーバーフローが未定義であることを意味します。これにより、さまざまな最適化が可能になります。たとえば、コンパイラは、i + 10 > i のような式が符号付き i に対して常に真であると想定します。この仮定は、符号付きオーバーフローが定義されていない場合にのみ有効です。これは、2 の補数演算を使用するときに i + 10 オーバーフローの場合、式が false であるためです。このオプションが有効な場合、符号付き数値の演算がオーバーフローするかどうかを判断する試みは、実際にオーバーフローを起こさないように慎重に記述する必要があります。このオプションにより、コンパイラは厳密なポインター セマンティクスを想定することもできます。オブジェクトへのポインターが与えられると、そのポインターにオフセットを追加しても同じオブジェクトへのポインターが生成されない場合、追加は未定義です。これにより、コンパイラは、ポインター p と符号なし整数 u に対して p + u > p が常に真であると結論付けることができます。この仮定は、2 の補数演算を使用して p + u がオーバーフローした場合に式が false になるため、ポインターのラップアラウンドが定義されていないためにのみ有効です。

-fwrapv オプションも参照してください。-fwrapv を使用すると、整数符号付きオーバーフローが完全に定義され、ラップされます。-fwrapv が使用されている場合、整数に対して -fstrict-overflow と -fno-strict-overflow の間に違いはありません。-fwrapv を使用すると、特定の種類のオーバーフローが許可されます。たとえば、コンパイラが定数の算術演算を行っているときにオーバーフローが発生した場合、オーバーフローした値は -fwrapv で引き続き使用できますが、それ以外の場合は使用できません。

-fstrict-overflow オプションは、レベル -O2、-O3、-Os で有効になります。

于 2012-10-04T14:11:37.863 に答える