目录

一printf了解准备:

printf打印顺序:

二printf打印i++,++i解释

1引例以及简单设想

2传参的反汇编:

一printf了解准备:

1printf打印顺序:

我们最开始用到的就是printf,在我接触到函数后,我开始从函数角度去看printf这个标准输出函数,才意识到“”双引号引起的是个格式字符串参数,而我们提供的变量则称为待打印项。我们在用打印函数的时候其实可以很明显感觉到我们是根据格式字符串自左向右打印到屏幕上。证明如下:

逗号在屏幕右边,这样就能证明printf的打印是把双引号引起的内容自左向右打印出来。而且%d和变量是一一对应的,第一个%d对应第一个变量i,第二个%d对应第二个变量i+1。按我们之前对函数的理解,函数都是我们把参数传过去才开始工作,而且传参是自右向左的(这一点后面汇编可以证得),当然如果我们的参数是表达式,运算顺序也是自右向左。

二printf打印i++,++i解释

1引例以及简单设想

int i = 10;printf("%d %d\n",i++,++i);

那上面代码的运行我预想应该是这样的

先算++i,前置++先运算后使用(传10给printf),运算后i为11

再算i++,先使用(传11给printf)i的值,再运算,运算后i为12

打印结果为11 11

实际结果为11 12

我原以为是传参数是计算一个表达式把结果传给printf,那表达式++i应该传的10,为什么是12呢?为什么传的第一个参数的值不是第一个表达式的结果而是最终i的值,为什么第二个参数的值又不是12呢?首先要说的是传值给函数不是运算完一个表达式传一个参数,而是将所有参数表达式运算完后,才开始传参。也就是说自右向左第一个前置++i运算后结果确实为11,但printf是调用的是参数表达式运算完后i的值,而i的值在下一个后置++中变成了12,这就是为什么大于结果中为12,而不是10的原因。那为什么打印i++又是11呢,因为i++先使用,后++的特点使得编译器会保存i++前的值,也就是11,printf调用的就是这个被保存的值。

其实上面的论证还是有许多疑点的,但是要更清楚地证明要看代码运行的反汇编,如果你不想了解反汇编,遇到这个printf打印i++,++i的组合也可以直接记忆成前置++,先运算,再使用i的值,但在函数传参中是等所有表达式运算完后,再传i的值给printf,而后置++则是先保存i++前的值,printf调用的就是这个被保存的值,而不是+1完后的值。

2下面我将介绍传参的反汇编:

第一行movdword ptr [i],0Ah : 把0Ah(这是十六进制的数字十,h不用理会)放入ptr[i](变量i中)

这步是int i=10;的汇编代码

第二行moveax,dword ptr [i]:把变量i的值放入寄存器eax中,

第三行addeax,1 :把寄存器eax的值加一

第四行movdword ptr [i],eax :把eax中的值放回变量i中去

上面执行了++i,上面就是把值放到变量i,然后放到寄存器eax中拿去加1,结果返回变量i中。

如图

第一行mov ecx,dword ptr [i] 把i的值放到寄存器ecx

第二行 movdword ptr [ebp-0D0h],ecx 把ecx的值放到一块内存空间中

第三行movedx,dword ptr [i]把i的值又复制存到edx中

第四行addedx,1 edx中的值加一

第五行movdword ptr [i],edx把edx中的值放回i中去

上面执行了i++,大家可以看出后置++比前置++多了第一行和第二行两个步骤(所以老师常说后置++比前置++要繁琐就是这个原因)在i+1前先把i的值存到内存空间[ebp-0D0h],这个空间的值是11。

上面说的都是i++,++i的运算过程,还没开始传参,但是我们可以得出,运算顺序呢确实是自右向左。

第一行和之前相同的是将i中的值放入寄存器eax中。

第二行指的是将eax上的值存到栈顶。(注意:push就是把数据放到栈顶上,可以理解为再存到内存中去)

第三行指的是将那块内存空间上的值放到寄存器ecx上。

第四行也就是把寄存器ecx的值放到内存栈顶。

首先printf都是传值调用,这个时候传参就是将数据复制一份再存到栈上去(我想这里可能会有读者问数据不是已经在内存了吗,为什么要再存一份,因为这是传值调用,是把数据再拷贝一份放到内存中给printf调用),传参是自右向左,从汇编上也可以看出,而且第一个push传的是++i中i的结果,这个i在经过两次运算后已经变成12,而第二个push传的是一块内存空间[ebp-0D0h]的值,而这个值是i++运算前的值,所以为11。

说了这么多其实有读者可能会说为什么不是printf的问题,那是否有办法判断是否是printf的问题呢,有,我们把i++,++i的值传给函数里的printf,看传给Printf函数的值和printf打印的结果是不是一样就行了,一样的话说明和printf的处理无关,如下图。

终于又完成了一篇,这篇涉及到了反汇编,其实都是简单的英文单词,理解一回生二回熟,如果有问题,请读者们在评论指出,我会及时回复的!