为什么在包含两者的表达式中在postfix之前评估前缀?

时间:2015-08-27 16:58:29

标签: java operator-precedence unary-operator

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

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

应优先于:

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

所以,鉴于下面的小片段,为什么:

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

打印3 vs 24而不是3 vs 20

精化

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

  1. 评估x--

  2. 评估--x

  3. 评估[--x] * 5

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

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

  6. 然后解决如下:

    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。

    P.S。 (如果在x--之前评估-x,则得到24,但由于运算符优先级而不应该是这种情况。)

    我是盲目的还是只是擅长数学或什么?

5 个答案:

答案 0 :(得分:5)

无论优先级规定的操作顺序如何,操作符的操作数始终从左到右进行评估。

以下是发生的事情:

  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"
  6. JLS, Section 15.7,声明:

      

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

答案 1 :(得分:0)

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

来自Java Docs:

  

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

以下是Java优先级的 good detailed article

答案 2 :(得分:0)

因此,Java编译器找到了这个表达式:

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

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

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

请注意,在这个阶段,操作数是不透明的实体:如果它们是变量(意味着从内存中读取),常量,方法调用,隐式取消装置或其他任何内容,那么它们并不重要 - 括号过程没有&#39我需要知道。

最后,当发出字节码时,编译器还有另一条要遵守的规则:操作数必须从左到右进行计算,以便从内存中读取和写入的操作(例如,变量递减/递增和方法调用)具有保证订单。因此,必须按照说明的顺序进行:

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--)

答案 3 :(得分:0)

运算符优先级与求值操作数的顺序无关。操作数始终从左到右进行评估。

运算符优先级确定如何计算表达式。例如,表达式4 * 5 + 6可能意味着(4 * 5) + 64 * (5 + 6)。因为*具有更高的运算符优先级,所以正确的优先级是第一个。

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

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

答案 4 :(得分:-1)

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

这就是为什么3和24