int x=1;
int y=2;
x ^= y ^= x ^= y;
我期望两个变量的值进行交换,但输出的结果是x=0且y=1。 在C语言中尝试后,得到了正确的结果。
int x=1;
int y=2;
x ^= y ^= x ^= y;
我期望两个变量的值进行交换,但输出的结果是x=0且y=1。 在C语言中尝试后,得到了正确的结果。
您的语句大致等同于以下展开形式:
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;
但是这是一种非常糟糕的交换两个变量的方法。使用一个临时变量是一个更好的选择。
关于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语言中这是未定义的行为,因为你在序列点之间两次修改了同一个变量。
x ^= y ^= x ^= y;
的行为在 C 语言中是未定义的。 - Prasoon Sauravx ^= y; y ^= x; x ^= y;
。而你在原帖中的代码会产生未定义的行为。如果它“能用”,那只是偶然。实际上,它是不能用的。 - AnT stands with Russia