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

Xsj*_*ado 20 java variables xor variable-assignment bitwise-operators

我正在尝试交换两个ints - 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);
Run Code Online (Sandbox Code Playgroud)

输出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);
Run Code Online (Sandbox Code Playgroud)

产量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);
Run Code Online (Sandbox Code Playgroud)

在这个阶段,输出成为了4, 3, 0, 4.现在我知道这04 ^ 4因为当时的x任务没有完成 - 为什么会发生这种情况?为什么不x ^= y实际分配7x变量以使其成为7 ^ 4最后一个赋值?

Cod*_*der 13

让我们试着扩展你的最后一个表达.

它评估为,

x = x^(y = y^ (x = x^y));
Run Code Online (Sandbox Code Playgroud)

需要注意的是表达式从评估,

它成为了,

x = 4 ^ (y = 3 ^ (x = 4 ^ 3));
Run Code Online (Sandbox Code Playgroud)

现在,问题变得明显了.对?

编辑:

为了清除混乱,让我试着通过从左到右的评估来解释我的意思.

int i = 1;
s = i + (i = 2) + i;
Run Code Online (Sandbox Code Playgroud)

现在,表达式将评估为,

s = 1 +    2    + 2;
Run Code Online (Sandbox Code Playgroud)

注意i在赋值的左边1,但在赋值的权利(和对于分配)被评估为2,因为评价是从左到右,当它到达表达式的第2和第3部分时,is值是2.


Rea*_*tic 5

评估顺序在JLS的第15章中定义.项目15.7.1说:

如果运算符是复合赋值运算符(第15.26.2节),那么对左侧操作数的计算包括记住左侧操作数表示的变量并获取并保存该变量的值以用于隐含的二进制操作.

为了进一步解释,他们有两个涉及任务的计算示例.这里的任务分配在运营商的左侧:

int i = 2;
int j = (i=3) * i;
System.out.println(j);
Run Code Online (Sandbox Code Playgroud)

并且他们明确地说结果是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);
Run Code Online (Sandbox Code Playgroud)

所述JLS指定两个打印应该产生12,并且不允许以产生6.即,这是因为分配给b是在右侧,左侧的值b(或者隐式左侧a+=操作)时,前值首先获取并保存该赋值,然后才执行括号内的操作.

在内部,表达式被分解为JVM操作,将值推送到"操作数堆栈".如果你这样考虑 - 在b = b + (b=3)b一次将值推到操作数堆栈上,然后执行(b = 3),然后将其值添加到从堆栈弹出的值(b的旧值),这将是有道理的.此时,左手b只代表"当它被推到堆栈时b的值是多少"而不是"b的当前值".