一、函数的调用过程
首先我们再viso studio中编写如下代码:
int add(int a, int b)
{
int c = a + b;
return c;
}
int main()
{
int d;
d = add(1,2);
printf("d=%d\r\n",d);
}
反汇编后得到如下汇编代码:
int add(int a, int b)
{
//入栈保护现场
00B117A0 push ebp
00B117A1 mov ebp,esp
00B117A3 sub esp,0CCh
00B117A9 push ebx
00B117AA push esi
00B117AB push edi
00B117AC lea edi,[ebp-0CCh]
00B117B2 mov ecx,33h
00B117B7 mov eax,0CCCCCCCCh
00B117BC rep stos dword ptr es:[edi]
00B117BE mov ecx,offset _549F54B0_consoleapplication1@cpp (0B1C008h)
00B117C3 call @__CheckForDebuggerJustMyCode@4 (0B1120Dh)
int c = a + b;
00B117C8 mov eax,dword ptr [a]
00B117CB add eax,dword ptr [b]
int c = a + b;
00B117CE mov dword ptr [c],eax
return c;
00B117D1 mov eax,dword ptr [c]
}
//add调用结束,出栈恢复现场
00B117D4 pop edi
00B117D5 pop esi
00B117D6 pop ebx
00B117D7 add esp,0CCh //回复sp指针,释放局部变量占用的内存
00B117DD cmp ebp,esp
00B117DF call __RTC_CheckEsp (0B11217h)
00B117E4 mov esp,ebp
00B117E6 pop ebp
00B117E7 ret
int main()
{
00CC1930 push ebp //基址指针寄存器
00CC1931 mov ebp,esp //保存当前堆栈,将栈顶指针存入ebp,当前栈顶和栈低重合
00CC1933 sub esp,0CCh //栈顶指针向下偏移0xccbyte,开辟临时变量的空间(栈空间)
00CC1939 push ebx
00CC193A push esi
00CC193B push edi
00CC193C lea edi,[ebp-0CCh]
00CC1942 mov ecx,33h
00CC1947 mov eax,0CCCCCCCCh
00CC194C rep stos dword ptr es:[edi]
00CC194E mov ecx,offset _549F54B0_consoleapplication1@cpp (0CCC008h)
00CC1953 call @__CheckForDebuggerJustMyCode@4 (0CC1221h)
int d;
d = add(1,2);
//从这里可以看出来,函数参数的入栈方向是自有向左
00CC1958 push 2 //最右边的形参入栈
00CC195A push 1 //最左边的形参入栈
00CC195C call add (0CC1186h)
00CC1961 add esp,8
00CC1964 mov dword ptr [d],eax
printf("d=%d\r\n",d);
00CC1967 mov eax,dword ptr [d]
00CC196A push eax
00CC196B push offset string "d=%d\r\n" (0CC7B30h)
00CC1970 call _printf (0CC104Bh)
00CC1975 add esp,8
}
//main函数调用结束,恢复现场
00CC1978 xor eax,eax
00CC197A pop edi
00CC197B pop esi
00CC197C pop ebx
00CC197D add esp,0CCh
00CC1983 cmp ebp,esp
00CC1985 call __RTC_CheckEsp (0CC122Bh)
00CC198A mov esp,ebp
00CC198C pop ebp
00CC198D ret
寄存器知识讲解
ESP:栈指针寄存器(extended stack pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶。
EBP:基址指针寄存器(extended base pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的底部。
X86/64 vc++的堆栈是从高地址向低地址生长,比如push一个32位寄存器入栈后,esp - 04H。
汇编指令讲解
sub 减计数器sub esp,04h表示将栈顶指针向下移动4个字节
总结
从上述C语言代码的反汇编来看,函数在调用的时候会对add的两个参数进行入栈操作,因此函数的形参也是需要占用栈空间的,只是形式参数占用的空间在函数调用结束后会自动释放。
函数的调用其实就是一个入栈和出栈的过程,在函数调用前调用push保护现场,在函数调用结束后调用pop恢复现场,并将sp指针恢复为函数调用前的状态,释放调用函数所占用的内存空间。