首先,就这个简单代码观察函数的调用过程
#include <stdio.h>
int Add(int a, int b);
int main()
{
int a = 10;
int b = 20;
printf("%d\n", Add(a, b));
return 0;
}
int Add(int a, int b)
{
return a + b;
}
很简单的代码,main函数中调用,Add函数。
这张图展示了函数的调用过程(蓝色代表main函数,绿色代表Add函数,图片从上到下由低地址向高地址增加),首先寄存器ebp指向一个内存的地址,之后将edp指向的地址赋值给寄存器esp使其指向同一块地址,之后esp减去一定数值使其指更低的地址,这时ebp与esp之间的一段栈空间便是为main开辟的内存,esp为栈顶,ebp为栈底。之后分别压栈ebx,esi,edi,将main函数所占的内存空间初始化为0xcc cc cc cc,函数中产生一定的偏移量为变量a,b分配内存将值赋给内存空间。创建形参,分别压栈eax,ecx,将a,b值传给形参的内存空间,压栈一块内存其中存储main函数中call指令下一条指令的地址(Add函数调用结束后可以通过这个地址返回原位置,执行main函数中下一条指令)。至此将进入Add函数中的指令。(每压栈一块地址,指向栈顶的esp减去4个字节即向上移动一次。ebx,esi等寄存器的压栈是使寄存器中保存该块内存的地址)
Add函数开始与main函数相同,将edp指向的地址赋值给寄存器esp使其指向同一块地址,之后esp减去一定数值使其指更低的地址,这时ebp与esp之间的一段栈空间便是为Add开辟的内存。之后分别压栈ebx,esi,edi,将Add函数所占的内存空间初始化为0xcc cc cc cc。函数从形参所占空间(eax,ecx所指向的空间)中取出形参a,b的值将其相加结果存于eax所指向的空间中。
Add函数开始返回调用,首先弹栈ebx,esi,edi释放他们所指向的空间。
然后将esp指向ebp所指向的位置,释放Add函数所占空间。
最后弹栈ebp,使其返回main的栈底,弹栈形参,esp,ebp返回继续维护main函数。
压栈eax将函数Add计算的结果返回给main函数,对函数Add的调用结束。
函数调用过程中,为函数开辟的用于本次函数调用中形参变量的保存,现场保护的栈空间,就称为函数栈帧。