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

发布于 2024-10-31 02:10:14 字数 584 浏览 9 评论 0原文

我发现这段代码可以使用 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

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

我的代码:

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

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

I found this code to swap two numbers without using a third variable, using the XOR ^ operator.

Code:

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

Now I changed the above code to this equivalent code.

My Code:

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

Now, I want to know, Why does my code give incorrect output?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

初心 2024-11-07 02:10:14

编辑:好的,明白了。

首先要指出的是,显然您无论如何都不应该使用这段代码。但是,当您展开它时,它相当于:(

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

如果我们使用更复杂的表达式,例如 foo.bar++ ^= i,则 ++ 非常重要code> 只计算一次,但在这里我相信它更简单。)

现在,操作数的计算顺序始终是从左到右,因此首先我们得到:

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

这(上面)是最重要的一步。 我们最终得到 36 作为最后执行的 XOR 运算的 LHS。 LHS 不是“评估 RHS 后 j 的值”。

^ 的 RHS 求值涉及“一级嵌套”表达式,因此变为:

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

那么看最深的嵌套,我们可以同时替换 ij:

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

... 变成

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

RHS 中对 j 的赋值首先发生,但结果无论如何都会在最后被覆盖,所以我们可以忽略它 - 没有对 进行进一步的评估j 在最终作业之前:

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

这现在相当于:

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

或者:

i = 36;
j = 36 ^ 36;

变成:

i = 36;
j = 0;

我认为这都是正确的,并且得到了正确的答案...向 Eric Lippert 道歉,如果有的话有关评估顺序的详细信息略有偏差:(

EDIT: Okay, got it.

The first point to make is that obviously you shouldn't use this code anyway. However, when you expand it, it becomes equivalent to:

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

(If we were using a more complicated expression such as foo.bar++ ^= i, it would be important that the ++ was only evaluated once, but here I believe it's simpler.)

Now, the order of evaluation of the operands is always left to right, so to start with we get:

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

This (above) is the most important step. We've ended up with 36 as the LHS for the XOR operation which is executed last. The LHS is not "the value of j after the RHS has been evaluated".

The evaluation of the RHS of the ^ involves the "one level nested" expression, so it becomes:

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

Then looking at the deepest level of nesting, we can substitute both i and j:

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

... which becomes

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

The assignment to j in the RHS occurs first, but the result is then overwritten at the end anyway, so we can ignore that - there are no further evaluations of j before the final assignment:

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

This is now equivalent to:

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

Or:

i = 36;
j = 36 ^ 36;

Which becomes:

i = 36;
j = 0;

I think that's all correct, and it gets to the right answer... apologies to Eric Lippert if some of the details about evaluation order are slightly off :(

空宴 2024-11-07 02:10:14

检查生成的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]

不正确的交换会生成以下代码:

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

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

Checked the generated IL and it gives out different results;

The correct swap generates a straightforward:

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]

The incorrect swap generates this code:

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

It's evident that the code generated in the second method is incorect, as the old value of j is used in a calculation where the new value is required.

友欢 2024-11-07 02:10:14

C# 将 jiji 加载到堆栈上,并存储每个 XOR 结果而不更新堆栈,因此最左边的 XOR 使用 j 的初始值。

C# loads j, i, j, i on the stack, and stores each XOR result without updating the stack, so the leftmost XOR uses the initial value for j.

苍景流年 2024-11-07 02:10:14

重写:

j ^= i;       
i ^= j;
j ^= i;

扩展 ^=

j = j ^ i;       
i = j ^ i;
j = j ^ i;

替换:

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

替换仅当/因为首先评估 ^ 运算符的左侧时才有效:

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

折叠 ^

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

对称地:

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

Rewriting:

j ^= i;       
i ^= j;
j ^= i;

Expanding ^=:

j = j ^ i;       
i = j ^ i;
j = j ^ i;

Substitute:

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

Substitute this only works if/because the left hand side of the ^ operator is evaluated first:

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

Collapse ^:

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

Symmetrically:

i = (i ^= j) ^ (j ^= i);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文