8
int func(int **a)
{
    *a = NULL;
    return 1234;
}

int main()
{
    int x = 0, *ptr = &x;
    *ptr = func(&ptr);      // <-???
    printf("%d\n", x);      // print '1234'
    printf("%p\n", ptr);    // print 'nil'

    return 0;
}

これは未定義の動作の例ですか、それともシーケンス ポイントに関係していますか? なぜ行:

*ptr = func(&ptr);

次のようには動作しません:

*NULL = 1234;

編集:gcc 4.7で出力「1234」と「nil」が得られることを忘れていました。

4

5 に答える 5

5

言語は、右辺の部分式func(&ptr)

*ptr = func(&ptr);

が最初に評価され、左側の部分式*ptrが後で評価されます (これは明らかに予想どおりです)。左辺は、を呼び出す前にfunc、最初に正当に評価できます。そして、これはまさにあなたの場合に起こったことです:*ptr呼び出しの前に、ptrまだ を指していたときに評価されましたx。その後、割り当て先が確定しました(つまり、コードが に割り当てられることがわかりましたx)。それが発生すると、変更ptrしても割り当て先は変更されなくなります。

したがって、評価の順序が指定されていないため、コードの即時の動作は指定されていません。ただし、考えられる評価スケジュールの 1 つは、NULL ポインター逆参照を引き起こすことによって、未定義の動作につながります。これは、一般に動作がundefinedであることを意味します。

このコードの動作を C++ 言語でモデル化する必要がある場合、この場合の評価プロセスはこれらの重要なステップに分割できると言えます。

1a. int &lhs = *ptr;      // evaluate the left-hand side
1b. int rhs = func(&ptr); // evaluate the right-hand side
2.  lhs = rhs;            // perform the actual assignment

(C 言語には参照がありませんが、内部的には代入の左辺の評価結果を格納するために「実行時にバインドされた左辺値」という同じ概念を使用します。) 言語仕様では、手順 1a と1b 任意の順序で発生します。1b が最初に発生することを期待していましたが、コンパイラは 1a から開始することを決定しました。

于 2013-06-14T19:15:50.593 に答える
1

関数の呼び出しはシーケンス ポイントですが、これは関数の副作用 (呼び出し自体) ではなく、パラメーターの評価 (呼び出し前) にバインドされます。

于 2013-06-14T19:15:53.277 に答える