一行上的多项分配未按预期工作
Multiple assignment on one line not working as expected
我正在尝试交换示例中的两个 int
s - x
和 y
,并在没有库函数的情况下在一行中完成。
所以我从这个开始:
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
。现在我知道 0
是 4 ^ 4
的结果,因为当时 x
赋值还没有完成——为什么会这样?为什么 x ^= y
实际上没有将 7
分配给 x
变量,以便它成为最后一次分配的 7 ^ 4
?
让我们尝试扩展您的最后一个表达式。
它的计算结果为,
x = x^(y = y^ (x = x^y));
请注意,表达式的计算范围是从 left 到 right,
它变成了,
x = 4 ^ (y = 3 ^ (x = 4 ^ 3));
现在,问题已经很明显了。对吗?
编辑:
为了消除混淆,让我尝试通过从左到右评估来解释我的意思。
int i = 1;
s = i + (i = 2) + i;
现在,表达式的计算结果为,
s = 1 + 2 + 2;
请注意,左侧赋值的i
是1
,但右侧赋值(和分配)被评估为 2
,因为评估是从左到右,当它来到表达式的第二和第三部分时,i
s 的值是 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".
我正在尝试交换示例中的两个 int
s - x
和 y
,并在没有库函数的情况下在一行中完成。
所以我从这个开始:
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
。现在我知道 0
是 4 ^ 4
的结果,因为当时 x
赋值还没有完成——为什么会这样?为什么 x ^= y
实际上没有将 7
分配给 x
变量,以便它成为最后一次分配的 7 ^ 4
?
让我们尝试扩展您的最后一个表达式。
它的计算结果为,
x = x^(y = y^ (x = x^y));
请注意,表达式的计算范围是从 left 到 right,
它变成了,
x = 4 ^ (y = 3 ^ (x = 4 ^ 3));
现在,问题已经很明显了。对吗?
编辑:
为了消除混淆,让我尝试通过从左到右评估来解释我的意思。
int i = 1;
s = i + (i = 2) + i;
现在,表达式的计算结果为,
s = 1 + 2 + 2;
请注意,左侧赋值的i
是1
,但右侧赋值(和分配)被评估为 2
,因为评估是从左到右,当它来到表达式的第二和第三部分时,i
s 的值是 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".