深入剖析C函数参数的结合顺序及a++和++a的区别

来源:互联网 发布:csgo全皮肤mac破解版 编辑:程序博客网 时间:2024/06/16 10:49

C语言函数参数的结合顺序

今天上课时老师提出了一个关于C语言的函数参数的结合顺序的问题以及a++和++a有什么区别的问题,为了弄清楚这个问题,我写了如下的代码反汇编进行分析:

#include <stdio.h>#include <stdlib.h>int func(int a, int b, int c, int d);int main(){    int a = 1;    int b = 2;    func(a++, ++a, ++a, a++);    system("pause");}int func(int a, int b, int c, int d){    printf("%d %d %d %d\n", a, b, c, d);    return 0;}

在这个程序中,我再调用func的那一行代码设置断点,并且进行反汇编,反汇编后的代码:

这里写图片描述

由于调用func函数时,传入的参数都是a,因此调用func函数的那一行代码反汇编后都是对a进行操作的代码,不容易判断出各个参数的执行顺序,所以我将调用func时改为传入四个不同的参数,修改代码如下:

这里写图片描述

重新反汇编:

这里写写图片描述

从上面的汇编代码就很容易看出来了,调用func这个函数时,会依次将d、c、b、a四个参数从内存中取出来,然后压入堆栈中,最后使用call调用func函数,因此可以推断C语言函数参数的执行顺序是从右至左的

a++及++a的区别

弄清楚了函数参数的执行顺序就可以继续通过反汇编弄清楚a++和++a的区别了。
还是使用最开始的代码:

这里写图片描述

根据上面参数执行顺序的规则,执行func(a++, ++a, ++a, a++)时参数从右至左执行,于是我进行判断,最右的a++是先使用值再自加,于是传入1,从右至左第二个a这时等于2,因为是前置++,所以判断传入3,从右至左第三个参数同理判断传入4, 从右至左第四个参数因为是后置++,因此判断传入4,最后执行完后a=5,所有我判断传入参数是4 4 3 1,但是执行结果:

这里写图片描述

为什么会产生这样的结果呢?通过对调用func的这一行代码进行反汇编:

这里写图片描述

通过参数从右向左执行的原则,可以推断出从右至左的a++、++a、++a、a++对应的汇编代码,从这里我们就看出来了a++和++a的区别:

a++:

这里写图片描述

++a:

这里写图片描述

从上面可以看出,++a是将a从内存中取出,存入寄存器,加1后再送回a所在的内存空间中;而a++则是先将a从内存中取出来,再存入ebp寄存器中的内容减去0DC所得值作为内存偏移地址所在的内存空间中,为什么要先将a存入另外的内存空间呢?继续分析参数执行完后的汇编代码:

这里写图片描述

在程序真正跳转到func之前,还执行了这些代码,这些代码的作用就是将函数的参数压入栈中,最后调用func函数,而func函数中则从栈中取出这些参数,这个过程就是参数传递的过程。在上述代码中,我们看第一个和第四个箭头所指的代码,这刚好对应第一个和第四个参数,它们是从分别从ebp-0DC和ebp-0E0地址所对应的内存中取出值,然后压入栈,而第二和第三个箭头对应的第二个和第三个参数,则是直接从a所在内存区域取出a的值,然后压入栈中。

从这里我们就可以看出a++和++a的区别了,a++会将a的值保存到内存的一个临时区域,或者说存放在一个临时变量中,然后a自加1,取a++的值时就从内存或这个临时变量中取出a的值,这时取出的值是a自加之前的值,最后再使用这个值;而++a则是直接将a的值自加1,使用它的值时也是从a所在内存中直接取出的。

对于结果为什么会是4 5 5 1,通过反汇编也可以很清楚的看出来了,调用func时是先从右至左执行完后才传入参数的,对于++a直接使用最后的a的值,对于a++则使用执行这条语句之前a的值,如示例代码中,执行第一个a++时,a的值为4,因此传入的值就是4,而最后执行完这四条语句后a的值为5,因此中间两个参数传入的值为5,所以最后的结果为4 5 5 1。

原创粉丝点击