a =(a ++)*(a ++)在Java中给出奇怪的结果

Mar*_*ius 45 java operator-precedence post-increment ocpjp

我正在攻读OCPJP考试,因此我必须了解Java的每一个奇怪的细节.这包括增量前和增量后运算符应用于变量的顺序.以下代码给出了奇怪的结果:

int a = 3;

a = (a++) * (a++);

System.out.println(a); // 12
Run Code Online (Sandbox Code Playgroud)

答案不应该是11吗?或者13?但不是12!

跟进:

以下代码的结果是什么?

int a = 3;

a += (a++) * (a++);

System.out.println(a);
Run Code Online (Sandbox Code Playgroud)

Boz*_*zho 110

第一次a++ a成为4.所以你有3 * 4 = 12.

(a在第2次之后变为5 a++,但是被丢弃,因为赋值a =会覆盖它)

  • @Marius:它并没有像被覆盖那样丢弃.第二个++*被*应用,它**将a增加到5 ...然后a的值被现在评估的赋值结果替换.您分配到您正在使用的同一变量的事实不会改变所涉及的任何运算符的行为. (6认同)
  • Java和C++之间的主要区别在于Java指定了更多的序列点,因此这样的代码实际上是可定义的,与C++不同,C++中的代码可以很容易地计算到任意数量的东西. (4认同)
  • 在这种情况下,我非常确定它是从左到右.您可以通过将第一个a ++替换为(one()+ a ++)而将第二个替换为(two()+ a ++)来证明它.制作一个()和两个( )返回0,并将println放在这些方法中.在运行时,你会看到在()之前调用one(). (2认同)

Rok*_*alj 31

你的陈述:

a += (a++) * (a++);
Run Code Online (Sandbox Code Playgroud)

相当于以下任何一个:

a = a*a + 2*a
a = a*(a+2)
a += a*(a+1)
Run Code Online (Sandbox Code Playgroud)

请改用其中任何一种.

  • *为什么它会返回12?* (5认同)
  • @RokKralj这是一个考试的理论问题.它应该是极端和模棱两可的.:) (2认同)

the*_*oop 25

a++表示'a的值,然后a增加1'.所以当你跑步

(a++) * (a++)
Run Code Online (Sandbox Code Playgroud)

首先a++评估第一个,然后产生值3. a然后增加1. a++然后评估第二个.a产生值4,然后再次递增(但现在这没关系)

所以这变成了

a = 3 * 4
Run Code Online (Sandbox Code Playgroud)

等于12.


Cod*_*aos 9

int a = 3;
a += (a++) * (a++);
Run Code Online (Sandbox Code Playgroud)

首先构建语法树:

+=
  a
  *
    a++
    a++
Run Code Online (Sandbox Code Playgroud)

要评估它从最外层的元素开始并递归下降.对于每个元素做:

  • 从左到右评估孩子
  • 评估元素本身

+=运营商是特殊的:它被扩展到类似left = left + right,但只计算表达式left一次.在右侧被评估为值之前,左侧仍被评估为值(而不仅仅是变量).

这导致:

  1. 开始评估 +=
  2. 评估赋值给变量的左侧a.
  3. 将变量评估为将在添加中使用a的值3.
  4. 开始评估 *
  5. 评估第一个a++.这将返回a的当前值3并设置a4
  6. 评估第二个a++.这将返回a的当前值4并设置a5
  7. 计算乘积:3*4 = 12
  8. 执行+=.左侧3在第三步评估,右侧评估12.所以它指定3 + 12 = 15 a.
  9. 最终价值a是15.

这里需要注意的一点是,运算符优先级对评估顺序没有直接影响.它只会影响树的形式,从而间接影响顺序.但是在树中的兄弟姐妹中,无论运算符优先级如何,评估始终是从左到右.


Kal*_*Kal 7

(a++) 是一个后增量,因此表达式的值为3.

(a++) 是后增量,所以表达式的值现在是4.

表达评估从左到右进行.

3 * 4 = 12 
Run Code Online (Sandbox Code Playgroud)

  • `3*4 = 12`:嘿,这就是我称之为无可置疑的事实XD. (2认同)

Toa*_*ast 7

每次使用++时,都会后续递增a.这意味着第一个a ++评估为3,第二个评估为4. 3*4 = 12.


zzz*_*Bov 5

对操作员的工作原理普遍缺乏了解.老实说,每个操作员都是语法糖.

您所要做的就是了解每个操作员背后的实际情况.假设如下:

a = b -> Operators.set(a, b) //don't forget this returns b
a + b -> Operators.add(a, b)
a - b -> Operators.subtract(a, b)
a * b -> Operators.multiply(a, b)
a / b -> Operators.divide(a, b)
Run Code Online (Sandbox Code Playgroud)

然后可以使用这些概括来重写复合运算符(为简单起见,请忽略返回类型):

Operators.addTo(a, b) { //a += b
  return Operators.set(a, Operators.add(a, b));
}

Operators.preIncrement(a) { //++a
  return Operators.addTo(a, 1);
}

Operators.postIncrement(a) { //a++
  Operators.set(b, a);
  Operators.addTo(a, 1);
  return b;
}
Run Code Online (Sandbox Code Playgroud)

你可以重写你的例子:

int a = 3;
a = (a++) * (a++);
Run Code Online (Sandbox Code Playgroud)

Operators.set(a, 3)
Operators.set(a, Operators.multiply(Operators.postIncrement(a), Operators.postIncrement(a)));
Run Code Online (Sandbox Code Playgroud)

哪个可以使用多个变量拆分:

Operators.set(a, 3)
Operators.set(b, Operators.postIncrement(a))
Operators.set(c, Operators.postIncrement(a))
Operators.set(a, Operators.multiply(b, c))
Run Code Online (Sandbox Code Playgroud)

这种方式肯定更加冗长,但很明显,您永远不会想要在一条线上执行两次以上的操作.


tec*_*rer 5

的情况下 :

int a = 3;  
a = (a++) * (a++); 

a = 3 * a++; now a is 4 because of post increment
a = 3 * 4; now a is 5 because of second post increment
a = 12; value of 5 is overwritten with 3*4 i.e. 12 
Run Code Online (Sandbox Code Playgroud)

因此我们输出为12.

的情况下 :

a += (a++) * (a++); 
a = a + (a++) * (a++);
a = 3 + (a++) * (a++); // a is 3
a = 3 + 3 * (a++); //a is 4
a = 3 + 3 * 4; //a is 5
a = 15
Run Code Online (Sandbox Code Playgroud)

这里要注意的要点是,在这种情况下,编译器从左到右求解,并且在后增量的情况下,增量前的值用于计算,并且当我们从左向右移动时,使用递增的值.