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

fru*_*kad 42 java operator-precedence

我有一个行代码,

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

我的预期产量是12 * (11 + 5) = 192,但我得到187.我知道内部的增量运算符()首先要解决,然后为什么首先解决外部的增量运算符?

Sot*_*lis 99

表达式从左到右进行评估.括号(和优先级)只表示分组,它们不表示评估的顺序.

所以

 11 * (12 + 5)
++a   ++a
Run Code Online (Sandbox Code Playgroud)

等于

187
Run Code Online (Sandbox Code Playgroud)


hac*_*cks 30

引自Eric Lippert的博客:

算术表达式的评估由三组规则控制:优先规则,关联规则和订单规则.

优先级规则描述了当表达式混合不同类型的运算符时,如何将表示不足的表达式括起来.

关联性规则描述了当表达式具有一堆相同类型的运算符时,如何将表示不足的表达式括起来.

评估规则的顺序描述了表达式中每个操作数的计算顺序.

较高的优先级导致操作数与操作符分组,并不意味着操作数的评估.评估的顺序决定了表达式中子表达式的评估顺序.


更新:

正如我所看到的,许多程序员都认为这种说法

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

将调用未定义的行为.是的,如果不保证运营商的操作数的评估顺序,它将调用UB.

但是在java编程语言的上下文中并非如此.它在java(以及C#)中具有良好定义的行为.Java语言规范声明:

15.7.评估订单

Java编程语言保证运算符的操作数似乎以特定的评估顺序进行评估,即从左到右.

例15.7.1-1.左手操作数首先被评估

在下面的程序中,*运算符有一个左手操作数,它包含对变量的赋值和包含对同一变量的引用的右操作数.引用产生的值将反映出赋值首先发生的事实.

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

该程序产生输出:

9
Run Code Online (Sandbox Code Playgroud)

不允许评估*操作员生产6而不是9.

但是,仍然java规范明确指出不要写这样的代码:

建议代码不要严格依赖于此规范.当每个表达式最多包含一个副作用时,代码通常更清晰,作为其最外层的操作,并且当代码不依赖于由于从左到右的表达式评估而出现的确切异常时.

  • 谢谢你的呐喊.我注意到博客文章讨论的是C#,但在这种情况下,C#和Java中的规则是相同的. (9认同)
  • @haccks:这些规则特别适用于具有中缀运算符的语言.纯后缀或纯前缀运算符的语言不需要它们.`2 3 4 +*`明确地是(3 + 4)*2 = 14. (6认同)

Pie*_*ard 10

从那个片段

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

有时,最简单的解决方案是使用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 = {})
Run Code Online (Sandbox Code Playgroud)
  1. a 增加1.(第3行)// a = 11
  2. a 增加1.(第7行)// a = 12
  3. 添加5a(第12行)// a = 17
  4. 1117(13号线)// A = 187

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