1. ホーム
  2. c#

この複合形式を使用した場合、XORによる値のスワップはなぜ失敗するのでしょうか?

2023-08-09 18:44:24

質問

XORを使用して、第3の変数を使用せずに2つの数値を交換するために、次のコードを発見しました。 ^ 演算子を使用しています。

コードです。

int i = 25;
int j = 36;
j ^= i;       
i ^= j;
j ^= i;

Console.WriteLine("i:" + i + " j:" + j);

//numbers Swapped correctly
//Output: i:36 j:25

さて、上記のコードをこのような等価なコードに変更しました。

私のコードです。

int i = 25;
int j = 36;

j ^= i ^= j ^= i;   // I have changed to this equivalent (???).

Console.WriteLine("i:" + i + " j:" + j);

//Not Swapped correctly            
//Output: i:36 j:0

今、知りたいのは なぜ私のコードは不正確な出力をするのでしょうか?

どのように解決するのですか?

編集部:わかりました。

最初のポイントは、明らかにこのコードはとにかく使うべきでないということです。しかし、これを展開すると、次のように等価になります。

j = j ^ (i = i ^ (j = j ^ i));

(のような複雑な表現を用いる場合、そのような表現が可能です。 foo.bar++ ^= i のような複雑な式を使う場合は ++ が一度だけ評価されることが重要ですが、ここではよりシンプルに考えています)。

さて、オペランドの評価順序は常に左から右なので、まず、次のようになります。

j = 36 ^ (i = i ^ (j = j ^ i));

これ(上記)が最も重要なステップです。 最後に実行されるXOR演算のLHSが36になってしまっていますね。LHS は "の値ではありません。 j の値ではありません。

のRHSの評価には、"1レベルの入れ子"式が含まれるので、次のようになります。

j = 36 ^ (i = 25 ^ (j = j ^ i));

次に、最も深いレベルの入れ子に注目すると、両方の ij :

j = 36 ^ (i = 25 ^ (j = 25 ^ 36));

... これは

j = 36 ^ (i = 25 ^ (j = 61));

への代入は j への代入が最初に起こりますが、結果は最後に上書きされるので、それを無視することができます。 j の更なる評価はありません。

j = 36 ^ (i = 25 ^ 61);

と同等になりました。

i = 25 ^ 61;
j = 36 ^ (i = 25 ^ 61);

または

i = 36;
j = 36 ^ 36;

となる。

i = 36;
j = 0;

I を考える はすべて正しく、正しい答えにたどり着きます。評価の順番に関する詳細が少しずれていたら、Eric Lippertに謝罪します :(