一行上的多项分配未按预期工作

Multiple assignment on one line not working as expected

我正在尝试交换示例中的两个 ints - xy,并在没有库函数的情况下在一行中完成。

所以我从这个开始:

int x = 4;
int y = 3;

System.out.println(x);
System.out.println(y);

x ^= y;

System.out.println(x);
System.out.println(y);

y ^= x;

System.out.println(x);
System.out.println(y);

x ^= y;

System.out.println(x);
System.out.println(y);

输出结果如预期的那样 4, 3, 7, 3, 7, 4, 3, 4。目前一切顺利。

接下来是:

int x = 4;
int y = 3;

System.out.println(x);
System.out.println(y);

y ^= (x ^= y);

System.out.println(x);
System.out.println(y);

x ^= y;

System.out.println(x);
System.out.println(y);

输出再次达到预期的 4, 3, 7, 4, 3, 4。目前还不错。

最后是这个:

int x = 4;
int y = 3;

System.out.println(x);
System.out.println(y);

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

System.out.println(x);
System.out.println(y);

在这个阶段输出变成了4, 3, 0, 4。现在我知道 04 ^ 4 的结果,因为当时 x 赋值还没有完成——为什么会这样?为什么 x ^= y 实际上没有将 7 分配给 x 变量,以便它成为最后一次分配的 7 ^ 4

让我们尝试扩展您的最后一个表达式。

它的计算结果为,

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

请注意,表达式的计算范围是从 leftright,

它变成了,

x = 4 ^ (y = 3 ^ (x = 4 ^ 3));

现在,问题已经很明显了。对吗?

编辑:

为了消除混淆,让我尝试通过从左到右评估来解释我的意思。

int i = 1;
s = i + (i = 2) + i;

现在,表达式的计算结果为,

s = 1 +    2    + 2;

请注意,左侧赋值的i1,但右侧赋值(和分配)被评估为 2,因为评估是从左到右,当它来到表达式的第二和第三部分时,is 的值是 2 .

让我们除以计算。

最里面的括号先执行,所有括号解析后,表达式从左到右执行。

x ^= (y ^= (x ^= y));   // initial statement

x = x^(y = y^ (x = x^y)); //equals to 

() have the highest precedence

x = x^(y = y^ (x = 3^4)); // first highest precedence () 

x = x^(y = y ^ (x = 7)); // still the first x is 3

x = 4 ^(y = 3 ^ (x = 7));  // now 3 ^ 7 =4

x = 4 ^ 4;  // now  3 ^ 7 =4

x= 0;

求值顺序在 JLS 的第 15 章中定义。项目 15.7.1 说:

If the operator is a compound-assignment operator (§15.26.2), then evaluation of the left-hand operand includes both remembering the variable that the left-hand operand denotes and fetching and saving that variable's value for use in the implied binary operation.

为了进一步解释,他们有两个涉及赋值的计算示例。这里赋值在运算符的左边:

int i = 2;
int j = (i=3) * i;
System.out.println(j);

而且他们明确说结果是9,不允许是6。即(i=3)都计算为3,i在与自身相乘前赋值为3。

但是在第二个例子中:

    int a = 9;
    a += (a = 3);  // first example
    System.out.println(a);
    int b = 9;
    b = b + (b = 3);  // second example
    System.out.println(b);

JLS规定两个打印都应该产生12,不允许产生6。也就是说,因为对b的赋值是在右边,所以左边的值b(或者implicit left a in the += operation), 赋值前的值先取出并保存,然后才执行括号内的操作。

在内部,表达式被分解为 JVM 操作,这些操作将值推送和弹出到 "operand stack"。如果你这样想——那在 b = b + (b=3)b 的值首先被压入操作数栈,然后执行 (b=3) 然后它的值被添加到弹出的值从堆栈(b 的旧值)中,它是有意义的。此时左手b只代表"What the value of b was when it was pushed on the stack"而不代表"current value of b".