3

GCC が予期しない結果を生成するコード フラグメントがあります。

(ターゲット i686-linux-gnu には gcc バージョン 4.6.1 Ubuntu/Linaro 4.6.1-9ubuntu3 を使用しています)

[test.c]

#include <stdio.h>

int *ptr;

int f(void)
{
    (*ptr)++;

    return 1;
}

int main()
{
    int a = 1, b = 2;

    ptr = &b;

    a = b++ + f() + f() ? b : a;

    printf ("b = %d\n", b);

    return a;
}

私の理解では、関数呼び出しにシーケンス ポイントがあります。後置インクリメントは、f() の前に実行する必要があります。

C99 5.1.2.3 を参照してください: 「... シーケンス ポイントと呼ばれ、以前の評価のすべての副作用は完全であり、後続の評価の副作用は発生していません。」

このテスト ケースでは、評価の順序が指定されていない可能性がありますが、最終結果は同じになるはずです。したがって、b の最終結果は 5 であると予想されます。しかし、このケースを 'gcc test.c -std=c99' でコンパイルした後、出力は b = 3 を示します。

次に、「gcc test.c -std=c99 -S」を使用して何が起こったかを確認します。

        movl    $1, 28(%esp)
        movl    $2, 24(%esp)
        leal    24(%esp), %eax
        movl    %eax, ptr
        movl    24(%esp), %ebx
        call    f
        leal    (%ebx,%eax), %esi
        call    f
        addl    %esi, %eax
        testl   %eax, %eax
        setne   %al
        leal    1(%ebx), %edx
        movl    %edx, 24(%esp)
        testb   %al, %al
        je      .L3
        movl    24(%esp), %eax
        jmp     .L4
.L3:
        movl    28(%esp), %eax
.L4:
        movl    %eax, 28(%esp)

GCC は f() の前に評価された値を使用し、2 回の f() 呼び出しの後に '++' 操作を実行するようです。

また、llvm-clang を使用してこのケースをコンパイルすると、結果は b = 5 と表示されます。これは予想どおりです。

ポストインクリメントとシーケンスポイントの動作に関する私の理解は間違っていますか?? それとも、これは GCC461 の既知の問題ですか??

4

2 に答える 2

4

Clang に加えて、参考として使用できるツールが他に 2 つあります。Frama-C の値分析KCCです。それらのインストール方法やこの目的での使用方法については詳しく説明しませんが、C プログラムの定義をチェックするために使用できます。行動。

どちらにも荒削りな部分がありますが、どちらも、プログラムの最後に未定義の動作がないことbは間違いないと考えています。5

Mini:~/c-semantics $ dist/kcc ~/t.c
Mini:~/c-semantics $ ./a.out 
b = 5

これは、Clang がそのように考えているよりもさらに強力な議論です (未定義の動作であったとしても、Clang は出力するプログラムを生成できるためb = 5)。

要するに、そのバージョンの GCC にバグが見つかったようです。次のステップは、SVN を調べて、SVN がまだ存在するかどうかを確認することです。

于 2012-08-21T19:57:51.300 に答える
3

このGCCバグを少し前に報告しましたが、今年初めに修正されました。http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48814を参照してください

于 2012-08-21T21:15:45.333 に答える