使用此复合形式时,为什么使用XOR交换值失败?

Jav*_*ram 76 c# swap xor

我发现这个代码使用XOR ^运算符交换两个数字而不使用第三个变量.

码:

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
Run Code Online (Sandbox Code Playgroud)

现在我将上面的代码更改为此等效代码.

我的代码:

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
Run Code Online (Sandbox Code Playgroud)

现在,我想知道,为什么我的代码输出不正确?

Jon*_*eet 77

编辑:好的,明白了.

要做的第一点是,显然你不应该使用这个代码.但是,当您展开它时,它将等同于:

j = j ^ (i = i ^ (j = j ^ i));
Run Code Online (Sandbox Code Playgroud)

(如果我们使用更复杂的表达式foo.bar++ ^= i,那么重要的++是只评估一次,但在这里我相信它更简单.)

现在,操作数的评估顺序总是从左到右,所以首先我们得到:

j = 36 ^ (i = i ^ (j = j ^ i));
Run Code Online (Sandbox Code Playgroud)

这(上图)是最重要的一步.我们最终得到36作为最后执行的XOR操作的LHS.LHS不是" jRHS评估后的价值".

对^的RHS的评估涉及"一级嵌套"表达式,因此它变为:

j = 36 ^ (i = 25 ^ (j = j ^ i));
Run Code Online (Sandbox Code Playgroud)

然后看着嵌套的最深层次,我们可以代替两个ij:

j = 36 ^ (i = 25 ^ (j = 25 ^ 36));
Run Code Online (Sandbox Code Playgroud)

......变成了

j = 36 ^ (i = 25 ^ (j = 61));
Run Code Online (Sandbox Code Playgroud)

jRHS中的赋值首先发生,但结果最后会被覆盖,所以我们可以忽略它 - j在最终赋值之前没有进一步的评估:

j = 36 ^ (i = 25 ^ 61);
Run Code Online (Sandbox Code Playgroud)

这相当于:

i = 25 ^ 61;
j = 36 ^ (i = 25 ^ 61);
Run Code Online (Sandbox Code Playgroud)

要么:

i = 36;
j = 36 ^ 36;
Run Code Online (Sandbox Code Playgroud)

哪个成了:

i = 36;
j = 0;
Run Code Online (Sandbox Code Playgroud)

认为这一切都是正确的,它得到了正确的答案......如果有关评估订单的一些细节略有偏离,请向Eric Lippert道歉:(

  • @Lasse:绝对.像这样的代码太可怕了. (8认同)
  • 为什么这个频率状态最好的C#专家之一*"你不应该在SO答案中使用这个代码"*?;-) (8认同)
  • @Fredrik:在这种情况下,我没有提出开始的代码.当有人询问你如何能够实现某些东西并且**起源*可怕的代码时,情况会有所不同:) (8认同)

SWe*_*eko 15

检查生成的IL并给出不同的结果;

正确的交换生成一个简单的:

IL_0001:  ldc.i4.s   25
IL_0003:  stloc.0        //create a integer variable 25 at position 0
IL_0004:  ldc.i4.s   36
IL_0006:  stloc.1        //create a integer variable 36 at position 1
IL_0007:  ldloc.1        //push variable at position 1 [36]
IL_0008:  ldloc.0        //push variable at position 0 [25]
IL_0009:  xor           
IL_000a:  stloc.1        //store result in location 1 [61]
IL_000b:  ldloc.0        //push 25
IL_000c:  ldloc.1        //push 61
IL_000d:  xor 
IL_000e:  stloc.0        //store result in location 0 [36]
IL_000f:  ldloc.1        //push 61
IL_0010:  ldloc.0        //push 36
IL_0011:  xor
IL_0012:  stloc.1        //store result in location 1 [25]
Run Code Online (Sandbox Code Playgroud)

不正确的交换生成此代码:

IL_0001:  ldc.i4.s   25
IL_0003:  stloc.0        //create a integer variable 25 at position 0
IL_0004:  ldc.i4.s   36
IL_0006:  stloc.1        //create a integer variable 36 at position 1
IL_0007:  ldloc.1        //push 36 on stack (stack is 36)
IL_0008:  ldloc.0        //push 25 on stack (stack is 36-25)
IL_0009:  ldloc.1        //push 36 on stack (stack is 36-25-36)
IL_000a:  ldloc.0        //push 25 on stack (stack is 36-25-36-25)
IL_000b:  xor            //stack is 36-25-61
IL_000c:  dup            //stack is 36-25-61-61
IL_000d:  stloc.1        //store 61 into position 1, stack is 36-25-61
IL_000e:  xor            //stack is 36-36
IL_000f:  dup            //stack is 36-36-36
IL_0010:  stloc.0        //store 36 into positon 0, stack is 36-36 
IL_0011:  xor            //stack is 0, as the original 36 (instead of the new 61) is xor-ed)
IL_0012:  stloc.1        //store 0 into position 1
Run Code Online (Sandbox Code Playgroud)

很明显,第二种方法中生成的代码是incorect,因为j的旧值用于需要新值的计算中.


C.E*_*uis 7

C#负载j,i,j,i栈,并且每个存储关于XOR结果,而不更新堆栈,所以最左边的XOR使用为初始值j.