Java 为什么在包含前缀和后缀的表达式中,前缀的计算要先于后缀?

Java 为什么在包含前缀和后缀的表达式中,前缀的计算要先于后缀?,java,operator-precedence,unary-operator,Java,Operator Precedence,Unary Operator,根据,一种操作,例如: 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对24而不是3对20 精化 假设给定的运算符优先级顺序,可以将代码段的第#2行分解为以下伪块(以前的评估值放在方括

根据,一种操作,例如:

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对24
而不是
3对20

精化

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

  • 评估
    x--

  • 评估
    --x

  • 评估
    [--x]*5

  • 评估
    [--x*5]+[x-->

  • 计算
    y=[--x*5+x-->

  • 然后,决议如下:

    在1之后
    返回5
    将x设置为4

    在2之后
    将x设置为3
    返回3

    3之后
    将3乘以5并返回15

    4之后
    将15添加到5,并返回20

    5后
    将y设置为20

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

    p.S. (如果计算x之前的x,则得到24,但由于运算符优先级的原因,情况不应如此)

    我是瞎了还是数学不好?

    x——发生在指令执行之后。 所以它就像是4*5+4;在执行这条语句之后,x再次减少到3


    这就是为什么3和24运算符的操作数总是从左到右求值,而不考虑优先级所规定的运算顺序

    发生的情况如下:

  • 评估
    --x
    。将
    x
    设置为
    4
    ,生成
    4
  • 评估
    (-x)*5
    <此时code>x为
    4
    ,因此计算
    4*5
    并生成
    20
  • 计算
    x--
    。产生
    4
    ,将
    x
    设置为
    3
  • 计算
    ((-x)*5)+x--。将
    20
    添加到
    4
    以生成
    24
  • 打印
    “3对24”
  • 报告指出:

    Java编程语言保证运算符的操作数按特定的求值顺序求值,即从左到右


    操作数从左到右求值,您认为这是从右到左。所以--x被设置为4,然后--x*5,也就是4*5等于20。然后--x*5+x--24。最后x-,计算为3

    从Java文档:

    Java编程语言保证 运算符似乎按特定的求值顺序求值, 即,从左到右


    下面是一个关于Java优先级的

    因此,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-)

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

    运算符优先级决定表达式的求值方式。例如,表达式
    4*5+6
    可能表示
    (4*5)+6
    4*(5+6)
    。因为
    *
    具有更高的运算符优先级,所以正确的运算符优先

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


    因此,在您的例子中,表达式相当于
    (-x)*5)+(x-)
    。然后可以对该表达式求值,操作数严格从左到右求值。

    可能重复的@AaronKurtzhals实际上不是该表达式的重复。在这个例子中,OP知道操作员是如何独立工作的;问题是当它们是同一表达式的一部分时,它们的求值顺序。这只是求值顺序,与运算符优先级本身没有太大关系。是否要排除赋值运算符?你分解了示例中的所有其他操作,即使这与这里无关,也可能是因为x也是任务的目标。我不确定你是否误解了正在发生的事情,或者只是没有很好地解释它,而是按照这个answ