GCC下宏扩展后的++i
听讨论的时候,遇到一题是关于++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先计算增一,再取增一后的值