如果括号具有更高的优先级,那么为什么首先解决增量运算符?

If parenthesis has a higher precedence then why is increment operator solved first?

我有单行代码,

int a = 10;
a = ++a * ( ++a + 5);

我的预期输出是 12 * (11 + 5) = 192,但我得到了 187。早知道()里面的自增算子要先解,那为什么要先解外面的呢?

表达式从左到右求值。括号(和优先级)只表示分组,不表示求值顺序。

所以

 11 * (12 + 5)
++a   ++a

等于

187

引自Eric Lippert's Blog

The evaluation of an arithmetical expression is controlled by three sets of rules: precedence rules, associativity rules, and order rules.

Precedence rules describe how an underparenthesized expression should be parenthesized when the expression mixes different kinds of operators.

Associativity rules describe how an underparenthesized expression should be parenthesized when the expression has a bunch of the same kind of operator.

Order of evaluation rules describe the order in which each operand in an expression is evaluated.

更高的优先级会导致使用运算符对操作数进行分组,并不意味着对操作数进行求值。求值顺序决定了表达式中子表达式的求值顺序。


更新:

据我所知,许多程序员都认为该声明

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

将调用未定义的行为。是的,如果不能保证运算符的操作数的评估顺序,它将调用 UB。

但这在 java 编程语言的上下文中是不正确的。它在 java(以及 C#)中具有明确定义的行为。 Java 语言规范指出:

15.7. Evaluation Order

The Java programming language guarantees that the operands of operators appear to be evaluated in a specific evaluation order, namely, from left to right.

示例 15.7.1-1。首先计算左手操作数

In the following program, the * operator has a left-hand operand that contains an assignment to a variable and a right-hand operand that contains a reference to the same variable. The value produced by the reference will reflect the fact that the assignment occurred first.

class Test1 {
    public static void main(String[] args) {
        int i = 2;
        int j = (i=3) * i;
        System.out.println(j);
    }
}

This program produces the output:

9

It is not permitted for evaluation of the * operator to produce 6 instead of 9.

但是,还是java规范明确规定不要写这样的代码:

It is recommended that code not rely crucially on this specification. Code is usually clearer when each expression contains at most one side effect, as its outermost operation, and when code does not depend on exactly which exception arises as a consequence of the left-to-right evaluation of expressions.

括号的作用是控制将哪些计算值用作后续操作的操作数。它们控制操作执行的顺序,仅在操作数被计算之后才能计算操作的范围内。考虑以下表达式:

(a()+b()) * (c()+d())
a() + (b()*c()) + d()

括号不需要(在 Java 中不能)影响调用 a()、b()、c() 和 d() 的顺序。它们可能会影响乘法是在调用 d() 之前还是之后执行,但仅在极少数情况下(例如 d() 调用 Java 本机接口,它以某种方式改变乘法中使用的数字舍入模式 Java 不知道)代码是否有任何方式知道或关心乘法是在 d() 之前还是之后执行的。

否则,重要的是在第一种情况下,一个加法运算将作用于a()和b(),另一个作用于c()和d();乘法将作用于 a()+b() 和 c()+d()。在另一种情况下,第一次乘法将作用于 b() 和 c(),第一次加法作用于 a() 和上述乘积,第二次加法作用于第一个和和 d()。

来自该片段

int a = 10;
a = ++a * ( ++a + 5);

有时,最简单的解决方案是使用 javap 来理解求值顺序:

 0: bipush 10 // push 10 onto the stack (stack={10})
 2: istore_1  // store 10 into variable 1 (a = 10, stack={})
 3: iinc 1, 1 // increment local variable 1 by 1 (a = 11, stack={})
 6: iload_1   // push integer in local variable 1 onto the stack (a = 11, stack = {11})
 7: iinc 1, 1 // increment local variable 1 by 1 (a = 12, stack={11})
 10: iload_1  // push integer in local variable 1 onto the stack (a = 12, stack = {12, 11})
 11: iconst_5 // load the int value 5 onto the stack (a = 12, stack = {5, 12, 11})
 12: iadd     // add 5 and 12 (a = 12, stack = {17, 11})
 13: imul     // multiply 17 and 11 (a = 12, stack = {})
  1. a 加 1。(第 3 行)// a = 11
  2. a 加 1。(第 7 行)// a = 12
  3. 5 添加到 a(第 12 行)// a = 17
  4. 11 乘以 17(第 13 行)// a = 187

(10 + 1 + 1 + 5) * 11 = 187

  int a = 10;
  a = ++a * ( ++a + 5);

以上类型的表达式总是以从左到右的方式求值,无论是 C 还是 JAVA

在这种情况下它是这样解决的 11*(12+5) 结果为 11*17 = 187 // w.r.t java

但如果它我们解决相同的表达式w.r.t C 语言

然后答案随着评估方式的改变而改变

在 c 中第一个 pre-increment/pre-decrement 发生,所以如果 "N" 没有次数在 inc/dec 之前 表达式中有然后 inc/dec 将首先发生 "N" 没有次数

然后相同的值将被替换为表达式中变量的每次出现并计算表达式值,然后 post increment/decrement 发生

即 a 递增到 11,然后再次递增到 12,因为表达式中 a 有两次递增,然后表达式被计算为

12*(12+5)=12*17=204 //w.r.t C语言