听讨论的时候,遇到一题是关于++i*++i的——这样的讨论意义不大,却也可以一窥编译器对相关问题的处理。
原题如下
相关代码说明如下
#include < stdio.h > #define POWER(X) X*X int main() { int ch = 8 ; int sum = POWER(ch ++ ); sum = POWER( ++ ch); return 0 ; }
如果添加printf输出,其第一个sum = 64,第二个sum = 144
将上面的进行汇编编译得到。
gcc -S test.c
程序main的主要汇编代码
main: pushl %ebp movl %esp, %ebp subl $ 16 , %esp movl $ 8 , - 8 (%ebp) movl - 8 (%ebp), %eax imull - 8 (%ebp), %eax movl %eax, - 4 (%ebp) addl $ 1 , - 8 (%ebp) addl $ 1 , - 8 (%ebp) addl $ 1 , - 8 (%ebp) addl $ 1 , - 8 (%ebp) movl - 8 (%ebp), %eax imull - 8 (%ebp), %eax movl %eax, - 4 (%ebp) movl $ 0 , %eax leave ret
少去进栈出栈的操作,i++*i++产生的汇编代码是
movl - 8 (%ebp), %eax imull - 8 (%ebp), %eax movl %eax, - 4 (%ebp) addl $ 1 , - 8 (%ebp) addl $ 1 , - 8 (%ebp)
++i*++i产生的汇编代码是
addl $ 1 , - 8 (%ebp) addl $ 1 , - 8 (%ebp) movl - 8 (%ebp), %eax imull - 8 (%ebp), %eax
由上面的代码可以看出
1)i++的自增操作是在整个乘法完成之后才进行的,而++i的操作则是在乘法之前进行。
2)movl和imull总是连续出现,进行乘法的时候,左右的操作数是从同一块内存取出来的,因此计算结果肯定是完全平方数。
找到一篇相似的文章《分析gcc下的i++与++i》(gfw),在后续讨论三个i++的情况。后面还有总结,先一并摘抄过来。
++i*i++*++i的汇编代码为
movl $ 8 , - 8 (%ebp) addl $ 1 , - 8 (%ebp) movl - 8 (%ebp), %eax imull - 8 (%ebp), %eax addl $ 1 , - 8 (%ebp) imull - 8 (%ebp), %eax movl %eax, - 4 (%ebp) addl $ 1 , - 8 (%ebp) leave ret
摘录
分析一下上面的代码,结论就很清晰了,所有的i++操作会在整个表达式计算完后才计算,除了最左边的乘法会从同一块内存取操作数外,后续的乘法的作操作数就是直接取的上次计算的结果。
总结一下: 1.从左向右计算 2.最左边的乘法从同一内存取操作数,因此肯定是完全平方数 3.所有的i ++ 可以替换成i 4.所有的 ++ i先计算增一,再取增一后的值