45

我正在为 OCPJP 考试而学习,所以我必须了解 Java 的每一个奇怪的细节。这包括前增量和后增量运算符应用于变量的顺序。以下代码给了我奇怪的结果:

int a = 3;

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

System.out.println(a); // 12

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

跟进:

以下代码的结果是什么?

int a = 3;

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

System.out.println(a);
4

15 回答 15

110

第一个a++ a变成 4 之后。所以你有3 * 4 = 12.

a在 2nd 之后变为 5 a++,但被丢弃,因为赋值a =覆盖了它)

于 2011-11-07T16:02:49.387 回答
31

您的声明:

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

等同于以下任何一种:

a = a*a + 2*a
a = a*(a+2)
a += a*(a+1)

改用其中任何一个。

于 2011-11-07T16:02:26.847 回答
25

a++表示“a 的值,然后 a 加 1”。所以当你跑

(a++) * (a++)

第一个首先a++被评估,并产生值 3。a然后增加 1。a++然后评估第二个。a产生 4 的值,然后再次递增(但现在这无关紧要)

所以这变成

a = 3 * 4

等于 12。

于 2011-11-07T16:04:02.083 回答
9
int a = 3;
a += (a++) * (a++);

首先构建语法树:

+=
  a
  *
    a++
    a++

评估它从最外面的元素开始并递归下降。对于每个元素,请执行以下操作:

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

运算符很特殊:它被扩展为类似的+=东西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. 的最终值为a15。

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

于 2011-11-08T11:20:19.230 回答
7

(a++)是一个后增量,所以表达式的值为 3。

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

表达式评估从左到右进行。

3 * 4 = 12 
于 2011-11-07T16:03:13.603 回答
7

每次使用 a++ 时,您都在后递增 a。这意味着第一个 a++ 计算结果为 3,第二个计算结果为 4。3 * 4 = 12。

于 2011-11-07T16:03:22.400 回答
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 

因此我们得到输出为 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

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

于 2011-11-09T06:06:06.450 回答
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)

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

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;
}

您可以重写您的示例:

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

作为

Operators.set(a, 3)
Operators.set(a, Operators.multiply(Operators.postIncrement(a), Operators.postIncrement(a)));

可以使用多个变量拆分:

Operators.set(a, 3)
Operators.set(b, Operators.postIncrement(a))
Operators.set(c, Operators.postIncrement(a))
Operators.set(a, Operators.multiply(b, c))

这样肯定会更冗长,但很明显,您永远不想在一行上执行两个以上的操作。

于 2011-11-07T19:05:22.257 回答
3

这是java代码:

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

这是字节码:

  0  iconst_3
  1  istore_1 [a]
  2  iload_1 [a]
  3  iinc 1 1 [a]
  6  iload_1 [a]
  7  iinc 1 1 [a]
 10  imul
 11  istore_1 [a]

这是发生的事情:

将 3 压入堆栈,然后从堆栈中弹出 3 并将其存储在 a 中。现在 a = 3 并且堆栈为空。

  0  iconst_3
  1  istore_1 a

现在它将值从“a”(3) 推入堆栈,然后递增 a(3 -> 4)。

  2  iload_1 [a]
  3  iinc 1 1 [a]

所以现在“a”等于“4”堆栈等于{3}。

然后它再次加载“a”(4),压入堆栈并增加“a”。

  6  iload_1 [a]
  7  iinc 1 1 [a]

现在“a”等于 5,堆栈等于 {4,3}

所以它最终从堆栈中弹出第一个两个值(4 和 3),将其相乘并将其存储回堆栈(12)。

 10  imul

现在“a”等于 5,堆栈等于 12。

最后是从堆栈中弹出 12 并存储在 a 中。

 11  istore_1 [a]

多田!

于 2011-11-08T18:39:03.983 回答
3

(a++)表示返回a和递增,所以 (a++) * (a++)表示 3 * 4

于 2011-11-07T16:05:34.863 回答
2

是 12。表达式从左边开始计算。所以它:

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

一旦第一部分 (3++) 被计算,a 为 4,所以在下一部分中,它执行 a = 3*4 = 12。请注意,最后一个后增量 (4++) 被执行但没有效果因为在此之后为 a 分配了值 12。

于 2011-11-07T16:07:11.473 回答
1

示例 1

int a = 3;

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

System.out.println(a); // 16

示例 2

int a = 3;

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

System.out.println(a); // 20

只是为了确保在哪里放置++表达式,该表达式会根据位置更改值。

于 2011-11-08T17:09:50.100 回答
1

第一个表达式大家已经解释清楚了,为什么a的值为12。

对于接下来的问题,对于不经意的观察者来说,答案是完全显而易见的:

17

于 2011-11-08T21:27:47.310 回答
0

前缀和后前缀增量的优先级高于乘法运算符。因此表达式被评估为 3*4。

于 2011-11-07T16:08:18.877 回答
0

如果您在下次使用 a 时使用 a++,则它会加一。所以你的所作所为

a = 3 * (3 + 1) = 3 * 4 = 12
于 2011-11-07T16:03:53.757 回答