所以,我知道很多C风格的语言都有递减(--
)和递增(++
)运算符,并且允许在整个表达式被评估之前或之后发生变异。
在退货时发生增量后会发生什么?我不是在考虑行为,而是在实施。
给定虚拟机(例如JavaScript / JVM)或物理机器(例如编译的C ++),生成的操作码是否如下所示? (假设基于堆栈的参数/返回。)
int x = 4, y = 8;
return f(++a) + y++;
变成这样:(也许?)
LOAD 4 A
LOAD 8 B
INC A
PUSH A
CALL F
POP INTO C
ADD C BY B
INC B
RET C
如果是这样,当表达式变得复杂时,这些语言中的这些操作如何决定嵌入增量的位置,甚至可能有点Lispish?
答案 0 :(得分:3)
Java运行时远离源代码,甚至远离字节码。一旦方法被JIT编译,生成的机器代码就会被积极地优化。但更重要的是,这种优化的细节超出了任何规范。如果您感兴趣,可以深入研究像HotSpot这样的实现,但是您从中学到的只是特定于平台,版本,内部版本号,JVM启动参数,甚至是JVM的单独运行。
答案 1 :(得分:2)
一旦代码被优化(通过C ++编译器或JIT),我会期望类似于以下内容:
来源:
int x = 4, y = 8;
return f(++x) + y++;
说明:
PUSH 5
CALL f
POP INTO A
ADD 8 to A
RET A
我列出的说明是正确的(除非我犯了一些愚蠢的错误),并且它们只需要我知道优化器能够进行的代码转换。基本上,未计算未使用的结果,并且在优化时计算具有已知结果的操作。
很有可能在特定架构上有一种更有效的方法,在这种情况下,优化器很有可能知道它并使用它。优化器经常超出我的期望,因为我不是专业的汇编程序员。在这种情况下,调用约定很可能在函数f
和此代码的情况下为返回值指定相同的位置。因此可能不需要任何POP - 返回寄存器/堆栈位置可以添加8个。此外,如果内联函数f
,则内联后将应用优化。
例如,如果f
返回input * 2
,那么整个函数可能会优化为:
RET 18
答案 2 :(得分:1)
您可以使用javap
准确查看编译器生成的内容。例如:
int x = 4, y = 8;
return f(++x) + y++;
被编译为这个字节码序列:
0: iconst_4
1: istore_1
2: bipush 8
4: istore_2
5: aload_0
6: iinc 1, 1
9: iload_1
10: invokevirtual #2; //Method f:(I)I
13: iload_2
14: iinc 2, 1
17: iadd
18: ireturn
当然,由JVM决定如何将其转换为汇编程序 - 请参阅Disassemble Java JIT compiled native bytecode了解如何在OpenJDK 7中查看结果。