为什么在Java中这个语句 x ^= y ^= x ^= y; 不能工作?

14
int x=1;
int y=2;
x ^= y ^= x ^= y;

我期望两个变量的值进行交换,但输出的结果是x=0且y=1。 在C语言中尝试后,得到了正确的结果。


4
在C语言中,这是未定义的行为,因为在一个序列点中同时修改了x和y两次。 - Matthew Flaschen
3
不要使用它。使用额外的临时变量来交换两个数更加高效,因为它不需要进行计算。 - Imre L
2
表达式 x ^= y ^= x ^= y; 的行为在 C 语言中是未定义的 - Prasoon Saurav
2
术语“正确”是错误的。 - Thorbjørn Ravn Andersen
2
你尝试做的正确的 C 语言版本如下:x ^= y; y ^= x; x ^= y;。而你在原帖中的代码会产生未定义的行为。如果它“能用”,那只是偶然。实际上,它是不能用的。 - AnT stands with Russia
显示剩余8条评论
2个回答

57

您的语句大致等同于以下展开形式:

x = x ^ (y = y ^ (x = x ^ y));

与C语言不同,在Java中,二元操作符的左操作数在右操作数之前被保证被求值。求值按以下方式进行:

x = x ^ (y = y ^ (x = x ^ y))
x = 1 ^ (y = 2 ^ (x = 1 ^ 2))
x = 1 ^ (y = 2 ^ (x = 3))
x = 1 ^ (y = 2 ^ 3)             // x is set to 3 
x = 1 ^ (y = 1)
x = 1 ^ 1                       // y is set to 1
x = 0                           // x is set to 0

您可以颠倒每个异或表达式的参数顺序,以便在再次评估变量之前完成赋值:

x = (y = (x = x ^ y) ^ y) ^ x
x = (y = (x = 1 ^ 2) ^ y) ^ x
x = (y = (x = 3) ^ y) ^ x 
x = (y = 3 ^ y) ^ x             // x is set to 3
x = (y = 3 ^ 2) ^ x
x = (y = 1) ^ x
x = 1 ^ x                       // y is set to 1
x = 1 ^ 3
x = 2                           // x is set to 2

这是一个更加简洁的版本,同样有效:

x = (y ^= x ^= y) ^ x;

但是这是一种非常糟糕的交换两个变量的方法。使用一个临时变量是一个更好的选择。


你确定吗?我以为赋值语句返回的是true,所以如果语句中使用了=而不是==,那么它们总是会执行? 这意味着你的第一个语句与x=x^true^true;是一样的。 - AaronM
1
@Aaron,不是的。JLS [§15.26](http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.26):“在运行时,赋值表达式的结果是变量在赋值发生后的值。”在Java中,如果在if条件语句中使用旧的`=`而不是`==`,只有当变量是布尔类型时才会编译通过。 - Matthew Flaschen

15

关于Java中的求值,Mark的观点是完全正确的。原因在于JLS §15.7.2.,在执行操作之前评估操作数和§15.7,它要求从左到右进行评估:

根据§15.26.2(复合赋值运算符),这等同于:

x = x ^ (y = y ^ (x = (x ^ y)));
我们从左至右进行求值,先计算两个操作数再执行运算。
x = 1 ^ (y = y ^ (x = (x ^ y))); // left of outer 
x = 1 ^ (y = 2 ^ (x = (x ^ y))); // left of middle 
x = 1 ^ (y = 2 ^ (x = (1 ^ y))); // left of inner
x = 1 ^ (y = 2 ^ (x = (1 ^ 2))); // right of inner
x = 1 ^ (y = 2 ^ (x = 3)); // inner xor (right inner assign)
x = 1 ^ (y = 2 ^ 3); // inner assign (right middle xor)
x = 1 ^ (y = 1); // middle xor (right middle assign)
x = 1 ^ 1; // middle assign (right outer xor)
x = 0; // outer xor (right outer assign)

注意,在C语言中这是未定义的行为,因为你在序列点之间两次修改了同一个变量。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接