使用这种复合形式时,为什么用 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
现在我将上面的代码更改为等效的代码。
我的代码:
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
编辑:好的,明白了。
首先要指出的是,显然您无论如何都不应该使用这段代码。但是,当您展开它时,它相当于:(
如果我们使用更复杂的表达式,例如
foo.bar++ ^= i
,则++
非常重要code> 只计算一次,但在这里我相信它更简单。)现在,操作数的计算顺序始终是从左到右,因此首先我们得到:
这(上面)是最重要的一步。 我们最终得到 36 作为最后执行的 XOR 运算的 LHS。 LHS 不是“评估 RHS 后
j
的值”。^ 的 RHS 求值涉及“一级嵌套”表达式,因此变为:
那么看最深的嵌套,我们可以同时替换
i
和j
:... 变成
RHS 中对
j
的赋值首先发生,但结果无论如何都会在最后被覆盖,所以我们可以忽略它 - 没有对进行进一步的评估j
在最终作业之前:这现在相当于:
或者:
变成:
我认为这都是正确的,并且得到了正确的答案...向 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:
(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:
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:
Then looking at the deepest level of nesting, we can substitute both
i
andj
:... which becomes
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 ofj
before the final assignment:This is now equivalent to:
Or:
Which becomes:
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 :(
检查生成的IL,它给出了不同的结果;
正确的交换会生成一个简单的代码:
不正确的交换会生成以下代码:
很明显,第二种方法中生成的代码是不正确的,因为 j 的旧值用于需要新值的计算中。
Checked the generated IL and it gives out different results;
The correct swap generates a straightforward:
The incorrect swap generates this code:
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.
C# 将
j
、i
、j
、i
加载到堆栈上,并存储每个XOR 结果而不更新堆栈,因此最左边的
XOR
使用j
的初始值。C# loads
j
,i
,j
,i
on the stack, and stores eachXOR
result without updating the stack, so the leftmostXOR
uses the initial value forj
.重写:
扩展
^=
:替换:
替换仅当/因为首先评估 ^ 运算符的左侧时才有效:
折叠
^
:对称地:
Rewriting:
Expanding
^=
:Substitute:
Substitute this only works if/because the left hand side of the ^ operator is evaluated first:
Collapse
^
:Symmetrically: