4

C では、未満の変数 (両方を と仮定int)がある場合、式を使用できます。 ij

i^=j^=i^=j

2 つの変数の値を交換します。たとえば、let int i = 3, j = 5; を計算した後i^=j^=i^=j、 がi = 5ありj = 3ます。

ただし、2 つの int ポインターを使用してこれをやり直すと*i^=*j^=*i^=*j、上記の例を使用して と にi = 0なりj = 3ます。


Cで

1

    int i=3, j=5;
    i^=j^=i^=j; // after this i = 5, j=3

2

    int i = 3, j= 5;
    int *pi = &i, *pj = &j;
    *pi^=*pj^=*pi^=*pj; // after this, $pi = 0, *pj = 5

JavaScript で

    var i=3, j=5;
    i^=j^=i^=j; // after this, i = 0, j= 3

JavaScript での結果は、これをより興味深いものにします

私のサンプルコード、ubuntuサーバー11.0およびgcc

    #include <stdio.h>
    int main(){
        int i=7, j=9;
        int *pi=&i, *pj=&j;
        i^=j^=i^=j;
        printf("i=%d j=%d\n", i, j);
        i=7, j=9;
        *pi^=*pj^=*pi^=*pj
        printf("i=%d j=%d\n", *pi, *pj);
    }


cでの未定義の動作

c の未定義の動作がこの質問につながる本当の理由でしょうか?

1

Windows 7 で Visual Studio 2005 を使用してコンパイルされたコードは、期待される結果を生成します (出力 i = 7、j = 9 を 2 回)。

2

ubuntu で gcc を使用してコンパイルされたコード( gcc test.c ) は、予期しない結果を生成します (出力 i = 7, j = 9 then i = 0, j = 9 )

3

ubuntu で gcc を使用してコンパイルされたコード( gcc -O test.c ) は、期待される結果を生成します (出力 i = 7,j = 9 を 2 回。)

4

3 に答える 3

8

i^=j^=i^=jC では未定義の動作です。

i2 つのシーケンス ポイント間で 2 回変更することにより、シーケンス ポイントの規則に違反しています。

これは、実装が自由に値を割り当てたり、プログラムをクラッシュさせたりできることを意味します。

同じ理由で、*i^=*j^=*i^=*jも未定義の動作です。

(C99, 6.5p2) 「オブジェクトは、前のシーケンス ポイントと次のシーケンス ポイントの間で、式の評価によって最大 1 回変更された格納値を持つものとします。」

于 2012-09-08T16:25:13.580 に答える
4

次のコードを検討してください。

 #include <stdio.h>

    int main() {
        int i=7, j=9;
        int *pi=&i, *pj=&j;
        i^=j^=i^=j;
        printf("i=%d j=%d\n", i, j);
        i=7, j=9;
        *pi^=*pj^=*pi^=*pj;
        printf("i=%d j=%d\n", *pi, *pj);

        return 0;
    }

コンパイルしようとするとwarning: unsequenced modification、最初の行に a が表示されます。@ouath が言ったように、それは明確に定義されていません。C11 標準によると、代入式は読み取り-変更-書き込み方式で機能します。この操作は、すべての CPU アーキテクチャでアトミックではありません。警告の詳細については、こちらをご覧ください。

*pi^=*pj^=*pi^=*pj;私の LLVM コンパイラに警告がないのは興味深いことです。

于 2012-09-08T14:18:45.337 に答える
3

Javascriptの結果によって追加された「より興味深い」側面については:

ouahの回答で説明されているように、式はCでは定義されていませんが、Javascriptでは明確に定義されています。ただし、Javascriptで式を評価する方法の規則は、期待したものではない場合があります。

ECMAscript仕様では、複合代入演算子は次のように評価されるとされています(ECMA-262 11.13.2):

プロダクションAssignmentExpression:LeftHandSideExpression @= AssignmentExpressionは、@上記の演算子の1つを表し、次のように評価されます。

  1. LeftHandSideExpressionを評価します。
  2. GetValue(Result(1))を呼び出します。
  3. AssignmentExpressionを評価します。
  4. GetValue(Result(3))を呼び出します。
  5. Result(2)とResult(4)に演算子@を適用します。
  6. PutValue(Result(1)、Result(5))を呼び出します。
  7. Result(5)を返します。

したがって、式i ^= j ^= i ^= jは次の手順で評価されます。

i = (3 ^ (j ^= i ^= j))

i = (3 ^ (j = (5 ^ i ^= j)))

i = (3 ^ (j = (5 ^ (i = 3 ^ j)))))

i = (3 ^ (j = (5 ^ (i = 3 ^ 5)))))

i = (3 ^ (j = (5 ^ (i = 6)))))

i = (3 ^ (j = (5 ^ 6))))

i = (3 ^ (j = 3))   // j is set to 3

i = (3 ^ 3)

i = 0               // i is set to 0

xor演算を使用して値を交換するトリックを回避するさらに別の理由。

于 2012-09-08T20:23:00.237 に答える