【问题标题】:Why is the operator precedence not followed here? [duplicate]为什么这里没有遵循运算符优先级? [复制]
【发布时间】:2015-05-23 14:42:23
【问题描述】:

在这段代码中:

int y = 10;
int z = (++y * (y++ + 5)); 

我的预期

第一个y++ + 5 将被执行,因为最内层括号的优先级。所以y 的值为11,这个表达式的值为15。然后++y * () 将被执行。所以 12 * 15 = 180。所以 z=180

我得到了什么

z=176

这意味着虚拟机从左到右不遵循运算符优先级。那么我对运算符优先级的理解是错误的吗?

【问题讨论】:

  • 优先级并不代表事情的执行顺序。
  • @Caramiriel 我有 99% 的把握这不是生产代码——这只是对语言和编译器的刺激和戳,OP 觉得他们发现了一些意想不到的东西。
  • @Loko 基本数学没有像y++ 这样的结构,因为基本数学没有可变变量(据我所知)。
  • 你对运算符优先级的理解是错误的;这是一个非常普遍的误解,许多初学者书籍对此的解释完全错误。这些文章是关于 C# 的,但这些原则同样适用于 Java;他们可能会帮助你。 ericlippert.com/tag/precedence
  • 要理解的关键是优先级决定了 operators 的执行顺序,而不是 operands 的执行顺序。 A() + B() * C() 保证乘法运算发生在加法之前。 A() 的调用既不是乘法也不是加法,因此对于A() 是否发生在B()C() 之前,优先级没有任何意义。跨度>

标签: java operators operator-precedence


【解决方案1】:

表达式 (++y * (y++ + 5));将被放置在类似这样的堆栈中:

1. [++y]
2. [operation: *]
3. [y++ + 5] // grouped because of the parenthesis

它会按照这个顺序执行,结果是

1. 10+1 = [11] // y incremented 
2. [operation: *]
3. 11+5 = [16] // y will only increment after this operation

表达式被计算为

11 * 16 = 176

【讨论】:

  • 如果您正在处理堆栈,[操作:*] 应该在两个输入之后 出现。它不会影响你在这里所说的重要部分,所以这只是一个小问题。
  • 我觉得这个答案很混乱。如果您正在考虑堆栈机器,则操作是:计算 ++y 的副作用和值并将结果压入堆栈,然后计算 y++ 的副作用和值并将结果压入堆栈,然后压入5 在堆栈上,然后从堆栈中弹出两个东西,将它们相加,然后将和压入堆栈,然后从堆栈中弹出两个东西,将它们相乘,然后将乘积放入堆栈。
  • 好的,使用后缀符号表示为[++y][y++][5]+*11 11 5 + *。在第一阶段解析期间,它将按顺序将 3 个值推入堆栈[5][11][11]。接下来它会找到+ 操作符,它会从堆栈中弹出两个值115,操作的结果,即16,将被推回堆栈,因此堆栈状态将是[16][11]。下一个标记是另一个运算符*,因此它会再次从堆栈中弹出2个值并执行操作:11 * 16(注意:弹出的第二个值将是第一个操作数)。
  • @OwenBringino 实际上,高级编程语言不会使用“堆栈”,它将实现语法规则。所以你的类比很容易崩溃。
【解决方案2】:

由于最内层括号的优先级,将首先执行 y++ + 5

优先级和评估顺序不是一回事。除了赋值表达式之外的所有二进制表达式都是从左到右计算的。因此,y++ 在右侧括号表达式之前计算。

【讨论】:

  • 但是我不明白在数学表达式中使用括号的意义
  • @EdmDroid 没有任何括号,++y * y++ + 5 将被评估为 (++y * y++) + 5 - 这是一个非常不同的表达式。
  • 对。非常感谢@nos
  • 我猜“优先”一词的选择会引起混淆。它实际上是绑定优先级,调节二进制操作链如何在语法上进行解析,而不是时间优先级。
  • 后者不在乘法的右边,它是二元乘法表达式的右边。规则很简单:计算左操作数,然后计算右操作数,然后计算二进制表达式本身。无需考虑重叠,表达式严格嵌套在树结构中。
【解决方案3】:

括号只是描述子表达式如何组合在一起。括号并不意味着它会首先被评估。相反,java 中的规则是严格从左到右评估每个子表达式

永远记住,求值顺序与运算符优先级关联性绝对无关。

Java Oracle 文档说:

15.7. Evaluation Order

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

因此,表达式(++y * (y++ + 5)) 将被计算为

temp1 = ++y = 11
temp2 = y++ + 5 = 11 + 5 = 16 
z = temp1*temp2 = 11*16 = 176  

进一步阅读:Eric Lippert's 博客、Precedence vs Associativity vs Order,详细解释了优先级关联性评估顺序。虽然此博客地址为,但同样适用于

【讨论】:

    【解决方案4】:
    1. 首先将执行++yy 将是 11
    2. 然后会执行y + 5(实际上y++ + 5可以写成5 + y++,解释为(5 + y)然后y++)。 z 将变为 11 * 16 = 176
    3. 计算完成后y 将变为12

    【讨论】:

    • 后缀 ++ 的优先级高于二进制 +。恰好表达式 y++ 的值是递增前 y 的值,但是 y 作为副作用递增。
    • 为什么5 + y++会被解释为(5 + y)++
    • 我的意思是(5 + y),然后是y++
    • 还是错了。 y 的副作用不一定会在 y + 5 之后发生。
    • 确实,这个答案具有误导性。虽然在这种情况下,您无法判断增量副作用是在添加之前还是之后发生,但您可以提出一个示例,让您可以区分差异。 Java 不允许将增量的副作用延迟到加法之后。 (想象一个世界,例如,加法可能会在溢出时引发异常;必须在可能引发异常之前观察增量的副作用,因为catch 可以观察增量变量。)跨度>
    【解决方案5】:

    计算按以下顺序进行

     z= (++10 * (10++ + 5))
     z= (11 * (11 + 5))//++ (prefix or postfix) has higher precedence than + or *
     z= (11 * 16)
     z= 176 
    

    【讨论】:

    • 对错误答案的四票赞成!! o.O
    • @hacks 公平地说,你没有做任何事情来帮助人们理解为什么这是错误的......
    • @DavidZ;你是否同意这样的说法:这里右边的部分会因为()而先被评估
    • 优先级与评估顺序无关。
    猜你喜欢
    • 2010-12-02
    • 1970-01-01
    • 2019-11-22
    • 1970-01-01
    • 1970-01-01
    • 2016-04-04
    • 2011-10-25
    • 2018-06-21
    • 1970-01-01
    相关资源
    最近更新 更多