直接上代码:
#include <stdio.h>
int main()
{
int i=0;
printf("%d,%d,%d,%d\n",i++,--i,++i,i++);
return 0;
}
你觉得上述打印结果会是什么呢?
按照变量自增自减的处理方式,后置自增或自减的返回值是通过寄存器得到的,在还没有对变量进行自增或自减时就已经将变量的值保存到了寄存器中,再结合函数自右向左的压参方式,我第一遍想到的结果是: 1 1 2 1 因为由两次后置++,如果都放在寄存器中,那么最后一次在寄存器中放的值会覆盖掉第一次在寄存器中放的值。(这是错误的理解)。
编译器运行结果:
1 2 2 0
为什么会是这样的结果呢?
要解决这个问题不得不进入到这段代码的汇编语言中进行分析,看编译器是如何处理这个函数的压参过程的,导致编译器打印出这样的结果。
这是编译器在处理printf("%d,%d,%d,%d\n",i++,--i,++i,i++);这条语句时的汇编语言。现在对这些汇编语言进行分析,看编译器都做了什么。。。。。
printf("%d,%d,%d,%d\n",i++,--i,++i,i++); // 函数自右向左进行压参入栈
01026CB5 mov eax,dword ptr [i] // 将变量 i 的值放在了寄存器eax中
01026CB8 mov dword ptr [ebp-0D0h],eax //将eax中的值,也就是变量 i 的值放在了一块临时内存区域内。(用这个临时区域存储了i++得到的值)
01026CBE mov ecx,dword ptr [i] //将变量 i 的值放在ecx中。
01026CC1 add ecx,1 //对ecx中的值加了1
01026CC4 mov dword ptr [i],ecx //将ecx中的值放在了变量 i 的存储区域中。(用经过 i++ 计算后值更新 i 的值,放在 i 的存储区域内)
01026CC7 mov edx,dword ptr [i] //将 i 的值放在了edx中。
01026CCA add edx,1 //对edx中的值加1。
01026CCD mov dword ptr [i],edx //将edx中的值存储在 i 的内存区域中,更新++i 计算后 i 的值。
01026CD0 mov eax,dword ptr [i] // 下面几个略去,分析方式和上面一样。
01026CD3 sub eax,1
01026CD6 mov dword ptr [i],eax
01026CD9 mov ecx,dword ptr [i]
01026CDC mov dword ptr [ebp-0D4h],ecx
01026CE2 mov edx,dword ptr [i]
01026CE5 add edx,1
01026CE8 mov dword ptr [i],edx
01026CEB mov esi,esp
01026CED mov eax,dword ptr [ebp-0D0h] //参数的值确定好了之后,开始进行压栈操作。 将临时区域中的值放在eax中,此时这个临时区域存的是 i++ 得到的值。
01026CF3 push eax //将 i++ 的结果压栈。
01026CF4 mov ecx,dword ptr [i] //到 i 的存储区域中取出 i 的值 前置++/--都是到变量自己的存储区域中取值。
01026CF7 push ecx //将 ++i 的值压栈
01026CF8 mov edx,dword ptr [i] //同上
01026CFB push edx
01026CFC mov eax,dword ptr [ebp-0D4h] //取出临时区域中的值,这个临时区域存的是 第一个参数 i++ 的值
01026D02 push eax //压栈
01026D03 push 102CCBCh
01026D08 call dword ptr ds:[10303A4h]
01026D0E add esp,14h
01026D11 cmp esi,esp
01026D13 call __RTC_CheckEsp (010212D0h)
由以上汇编代码的解释你应该可以理解为什么会打印出 1 2 2 0的结果了吧。。。。
总结:
一个函数的参数是变量的多次自增自减,那么编译器在处理的时候,对函数的参数自左向右处理,将后置++/--的值放在了一个临时区域内(而不是用寄存器存),然后再到变量真正的存储区域进行+1/-1。 将前置++/--的值直接经过计算将结果存入到变量的存储区域内。