您的位置:首页 > 其它

函数调用过程分析

2017-04-05 16:51 232 查看
编译器一般使用堆栈实现函数调用,Windows为每个线程维护一个堆栈,堆栈大小可以设置,编译器可以使用堆栈存放每个函数参数,局部变量等。由于函数调用经常会被嵌套,故同一时刻,堆栈中存储多个函数的信息,每个函数会占一个连续区域,一个函数占用的区域称为帧,编译器是从高地址开始使用堆栈,在多线程任务下,CPU的堆栈指针指向的存储器区域就是当前使用的堆栈。切换线程时,把堆栈指针设为当前线程所在堆栈的栈顶地址。具体而言,函数堆栈用于:

1 进入函数前,保存返回地址和环境变量;

将函数调用语句下一条语句的地址保存到在栈中,对实参表从后向前,一次计算出实参的值,并且将值压栈,跳转到函数体处。

2 进入函数后,保存实參或者局部变量

如果函数体中定义了变量,将变量压栈,将每一个形参以栈中对应的实参值取代,执行函数体的功能体。将函数体中的变量、保存到栈中的实参值,依次从栈中取出,释放栈空间>

返回过程执行的是函数体中的return语句。其过程是从栈中取出刚开始调用函数时压入的地址,跳转到函数的下一条语句。

下面举例:

#include <stdio.h>
int func(int param1 ,int param2,int param3)
{
int var1 = param1;
int var2 = param2;
int var3 = param3;

printf("var1=%d,var2=%d,var3=%d",var1,var2,var3);
return var1;
}

int main(int argc, char* argv[])
{
int result = func(1,2,3);//×调用

return 0;
}
注意:EBP是指向栈底的指针,在过程调用中不变,又称为帧指针。ESP指向栈顶,程序执行时移动,ESP减小分配空间,ESP增大释放空间,ESP又称为栈指针

1 函数Main执行,main各个参数从右到左逐步入栈,最后压入返回地址

2 执行×处调用,3个参数从右到左压入堆栈,栈内分布:



3 然后是返回地址入栈:



4 通过跳转指令进入被调用函数后,EBP入栈,然后把当前ESP的值给EBP,对应的汇编指令:

push ebp
mov ebp esp


5 开始执行赋值:

mov 0x8(%ebp), %eax
mov %eax, -0x4(%ebp)把[ebp+0x8]地址里的内容赋值给eax,即把param1的值赋值给eax,再把eax的值放到[ebp-4]这个地址即把eax值赋值var1这样完成 int var1 = param1,其他变量雷同。



6 输出结构后最后return 1,其汇编:

mov -0x4(%ebp) %eax通过eax寄存器保存返回值
7 函数调用完毕,局部变量var3,var2,var1一次出栈,EBP恢复原值,返回地址出栈,找到原执行地址,param1,param2,param3依次出栈,函数调用执行完毕。

附录:WIN32下的4种调用:

a _cdecl,它是C/C++默认调用方式,实參是以参数列表从右到左依次入栈,函数堆栈由调用方维护,主要应用于带有可变参数的函数上

b _stdcalll,是WIN API的调用约定,实參是以参数列表从右到左依次入栈,函数堆栈由调用方维护,但是带有可变参数的函数上即使显示指定_stdcall编译器也会自动改成c_cdecl

c _thiscall,是类的非静态成员函数的调用约定,不能在含有可变参数的函数上否则编译出错,实參是以参数列表从右到左依次入栈,函数堆栈由调用方维护,含有this指针,不是存放在函数堆栈而是CPU寄存器

d _fastcall,它的实參不是存放在函数堆栈而是CPU寄存器,所以不需要入栈出栈,堆栈释放,可以快速调用
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  函数调用