在调用任何一个函数时,系统都会为其分配内存空间,便于它存储临时变量或者别的一些东西,下面我就来简单介绍一下函数在调用时它的内存情况。即函数栈帧。
以此段代码为例:(此例是在vs2013下实现的)
int Add(int x, int y)
{
int sum = x + y;
return sum;
}
int main()
{
int x = 10;
int y = 20;
int ret = 0;
ret = Add(x, y);
printf("%d\n", ret);
system("pause");
return 0;
}
先来介绍两个寄存器:esp(栈顶指针),ebp(栈底指针)。这两个指针是来维护栈帧的范围。在调用main函数之前,这两个指针已经存在。
009B3E60 push ebp
009B3E61 mov ebp,esp
009B3E63 sub esp,0E4h
009B3E69 push ebx
009B3E6A push esi
009B3E6B push edi
009B3E6C lea edi,[ebp-0E4h]
009B3E72 mov ecx,39h
009B3E77 mov eax,0CCCCCCCCh
009B3E7C rep stos dword ptr es:[edi]
这段反汇编的意思是将从ebp-0E4h开始往下赋值,赋39h次0CCCCCCCCh。
int x = 10;
009B3E7E mov dword ptr [x],0Ah
int y = 20;
009B3E85 mov dword ptr [y],14h
int ret = 0;
009B3E8C mov dword ptr [ret],0
ret = Add(x, y);
009B3E93 mov eax,dword ptr [y]
009B3E96 push eax
009B3E97 mov ecx,dword ptr [x]
009B3E9A push ecx
009B3E9B call _Add (09B11F4h)
这里需注意,在执行call指令时,它会将call指令的下一条指令的地址自动压入堆栈!以便现场恢复!
009B3C00 push ebp
009B3C01 mov ebp,esp
009B3C03 sub esp,0CCh
009B3C09 push ebx
009B3C0A push esi
009B3C0B push edi
009B3C0C lea edi,[ebp-0CCh]
009B3C12 mov ecx,33h
009B3C17 mov eax,0CCCCCCCCh
009B3C1C rep stos dword ptr es:[edi]
int sum = x + y;
009B3C1E mov eax,dword ptr [x]
009B3C21 add eax,dword ptr [y]
009B3C24 mov dword ptr [sum],eax
return sum;
此段汇编表明,在进行加法运算时,是通过寄存器eax来进行运算。
009B3C27 mov eax,dword ptr [sum]
将sum的值赋给eax寄存器,让寄存器带回。
009B3C2A pop edi
009B3C2B pop esi
009B3C2C pop ebx
009B3C2D mov esp,ebp
009B3C2F pop ebp
009B3C30 ret
执行ret指令时,会将当前esp所指向的内容弹出,并作为地址将程序执行跳转到该地址出。
009B3EA0 add esp,8 程序开始从这个地址处开始执行。
009B3EA0 add esp,8
009B3EA3 mov dword ptr [ret],eax
到此,Add函数将加法运算所得到的结果通过寄存器eax返回给了main函数,存入ret中。
以上就是一个函数调用另一函数时函数栈帧所发生的变化。在对栈的使用时,是先使用高地址在使用低地址的,在调用函数传参时,从右往左传。