【问题标题】:Why is prefix evaluated before postfix in an expression containing both?为什么在包含两者的表达式中,前缀在后缀之前被评估?
【发布时间】:2015-08-27 16:58:28
【问题描述】:

根据the Oracle operator precedence specification,一个操作如:

x-- // Returns x, then subtracts 1 from x.

应该优先于:

--x // Subtracts 1 from x, then returns x.

那么,鉴于下面的小sn-p,为什么会这样:

int x = 5;
int y = --x * 5 + x--;
System.out.println(x+" vs "+y);

打印3 vs 24 而不是3 vs 20

阐述

假设给定的运算符优先顺序,可以将 sn-p 的第 2 行分解为以下伪块(先前评估的值放在方括号中):

  1. 评估x--

  2. 评估--x

  3. 评估[--x] * 5

  4. 评估[--x * 5] + [x--]

  5. 评估y = [--x * 5 + x--]

然后解决如下:

1 之后 returns 5 sets x to 4

2 之后 sets x to 3 returns 3

3 之后 multiplies 3 by 5 and returns 15

4 后 adds 15 to 5 and returns 20

5 后 sets y to 20

为什么返回值是24而不是20。

附言 (如果您在 x-- 之前评估 --x ,您会得到 24,但由于运算符优先级,它不应该是这种情况。

我是盲人还是数学不好?

【问题讨论】:

  • @AaronKurtzhals 实际上不是那个的副本。在这一点上,OP 知道操作员是如何独立工作的;问题是当它们是同一表达式的一部分时,它们的求值顺序。
  • 这只是评估的顺序,与运算符优先级本身并没有太大关系。

标签: java operator-precedence unary-operator


【解决方案1】:

运算符的操作数总是从左到右进行计算,而不管优先级规定的操作顺序如何。

会发生什么:

  1. 评估--x。将x 设置为4,产生4
  2. 评估(--x) * 5。此时x4,因此计算4 * 5 并产生20
  3. 评估x--。产生4,将x 设置为3
  4. 评估((--x) * 5) + x--;。将20 添加到4 以产生24
  5. 打印"3 vs 24"

JLS, Section 15.7,声明:

Java 编程语言保证运算符的操作数看起来是按照特定的求值顺序进行求值的,即从左到右。

【讨论】:

  • 您想排除赋值运算符吗?您分解了示例中的所有其他操作,即使它在这里不相关,如果 x 也是分配的目标。
【解决方案2】:

运算符优先级与计算操作数的顺序无关。操作数总是从左到右计算。

运算符优先级决定了表达式的计算方式。例如,表达式4 * 5 + 6 可能表示(4 * 5) + 64 * (5 + 6)。因为* 的运算符优先级更高,所以正确的是第一个。

前缀和后缀递减和递增都具有较高的运算符优先级,因此--x * 5 表示(--x) * 5 而不是--(x * 5)。显然,后一个表达式会产生编译器错误,因为x * 5 不是变量。这就是前缀和后缀递减和递增具有高运算符优先级的原因:将--x * 5 评估为--(x * 5) 是没有意义的。

因此,在您的情况下,表达式等效于((--x) * 5) + (x--)。然后可以计算此表达式,其中操作数严格从左到右计算。

【讨论】:

  • 这个答案的想法是正确的,但你并没有特别好地解释涉及前增量和后增量运算符的具体情况。此外,--(x * 5) 不是有效的 Java 代码。
【解决方案3】:

操作数是从左到右计算的,你认为它是从右到左。所以 --x 被设置为 4 然后 --x*5 即 4*5 为 20。然后 --x*5 + x-- 使其成为 24。最后 x-- 被评估为 3。

来自 Java 文档:

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

这是关于 Java 优先级的 good detailed article

【讨论】:

    【解决方案4】:

    所以,Java 编译器会找到这个表达式:

    int x = 5;
    int y = --x * 5 + x--;
    

    并且必须决定生成字节码的操作顺序。一行中有四个运算符:--、*、+ 和--,而不是一个括号,所以好的编译器会检查它的优先级表并把你这个懒惰的程序员省略的括号:

    ((--OP1) * OP2) + (OP3--) 
    

    请注意,在这个阶段,操作数是不透明的实体:它们是变量(意味着从内存中读取)、常量、方法调用、隐式拆箱或其他任何东西都没有关系 - 括号过程不需要知道。

    最后,当发出字节码时,编译器还需要遵守另一条规则:操作数必须从左到右进行计算,以便读取和写入内存的操作(例如变量减量/增量和方法调用)具有保证订单。所以在指令序列中一定是这样的:

    OP1
    ...
    OP2
    ...
    OP3
    

    让我们使用IntegerVariable 来面向对象,看看更直观的示例:

    public class IntegerVariable {
    
        private int val;
    
        public IntegerVariable(int val) {
            this.val = val;
        }
    
        public int getAndDecrement() {
            return val--;
        }
    
        public int decrementAndGet() {
            return --val;
        }
    
        public int get() {
            return val;
        }
    
        public static IntegerVariable Int(int val) {
            return new IntegerVariable(val);
        }
    
        public static void main(String... args) {
            IntegerVariable x = Int(5);
            int y = x.decrementAndGet() * Int(5).get() + x.getAndDecrement();
        }
    }
    

    如果在get()decrementAndGet()getAndDecrement() 设置断点,您可以清楚地看到它们之间的区别

    • 评估顺序,可通过使用虚拟调用进行调试。求值顺序是从左到右的,保证在操作发生之前操作数被完全求值
    • 运算符优先级,基本上只是把你省略的括号:y = ((--x) * 5) + (x--)

    【讨论】:

    • 这可能会回答这个问题,但它远非立即显而易见的方式或原因(你至少应该添加一些解释),并且“这是你可以得到答案的一种方式" 与 "这就是 Java 获得答案的方式(以及它的文档记录的地方)" 不同。
    【解决方案5】:

    x-- 发生在指令执行之后。 所以它就像 4*5+4;并且在执行完这条语句后,x 再次递减到 3。

    这就是为什么 3 和 24

    【讨论】:

    • 我不确定您是否误解了正在发生的事情或者只是没有很好地解释它,但是按照这个答案,我认为x-- + x--10,而实际上是@ 987654323@(用于x = 5)。检查其他答案。
    • 我选词不好
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-08
    相关资源
    最近更新 更多