先来看一个简单的例子:
#include <stdio.h>
#include <stdlib.h>
int sum(int _a, int _b)
{
int c = 0;
c = _a + _b;
return c;
}
int main()
{
int a = 10;
int b = 20;
int ret = 0;
ret = sum(a, b);
printf("%d\n", ret);
system("pause");
return 0;
}
我们知道:main函数也是由一个函数调用而来,开辟的一个函数栈针,调用main函数的是mainCRTStartup()函数,而该函数也有一个运行时堆栈,用esp【栈顶低地址】、ebp【栈底高地址】两个寄存器来维护。
现在编译上述代码,反汇编代码如下【vc++6.0开发环境下的反汇编代码】
//如下是从vc++6.0中截取的汇编指令
--- f:\vc\a\a.cpp --------------------------------------------------------------------------------------------------------
10:
11: int main()
12: {
00401060 push ebp 【压栈:把ebp寄存器里面的地址压到栈顶,此时esp移动到栈顶】
00401061 mov ebp,esp 【让ebp指向esp处,把esp地址赋给ebp】
00401063 sub esp,4Ch 【esp 指向 esp - 4Ch 处】
00401066 push ebx 【压栈:把ebx内容压到栈顶】
00401067 push esi 【压栈:把esi内容压到栈顶】
00401068 push edi 【压栈:把edi内容压到栈顶】
00401069 lea edi,[ebp-4Ch]
0040106C mov ecx,13h
00401071 mov eax,0CCCCCCCCh
00401076 rep stos dword ptr [edi] 【上述四条指令意思是:将指定区域全部初始化为CCCC形式】
13: int a=10;
00401078 mov dword ptr [ebp-4],0Ah 【把 10 赋给 a 变量所在空间】
14: int b=20;
0040107F mov dword ptr [ebp-8],14h 【把 20 赋给 b 变量所在空间】
15: int ret=0;
00401086 mov dword ptr [ebp-0Ch],0 【把 0 赋给 ret 变量所在空间】
16:
17: ret=sum(a,b); 【调用函数前先传参】
0040108D mov eax,dword ptr [ebp-8] 【把 b 变量内容赋给 eax寄存器】
00401090 push eax 【压栈:把eax内容压到栈顶】
00401091 mov ecx,dword ptr [ebp-4] 【把 a 变量内容赋给 ecx寄存器】
00401094 push ecx 【压栈:把ecx内容压到栈顶】
00401095 call @ILT+0(sum) (00401005) 【调用call指令之前,编译器悄悄地把call下一条指令地址保存在栈顶了】
0040109A add esp,8 【根据保存的地址CPU执行此条指令,让esp指向 esp+8 处】
0040109D mov dword ptr [ebp-0Ch],eax 【把eax保存的计算结果值放到ret变量空间内】
18: printf("%d\n",ret);
004010A0 mov edx,dword ptr [ebp-0Ch] 【接下来继续执行相关指令。。。】
004010A3 push edx
004010A4 push offset string "%d\n" (00424024)
004010A9 call printf (00401200)
004010AE add esp,8
19:
20: system("pause");
004010B1 push offset string "pause" (0042401c)
004010B6 call system (004010f0)
004010BB add esp,4
21: }
004010BE pop edi
004010BF pop esi
004010C0 pop ebx
004010C1 add esp,4Ch
004010C4 cmp ebp,esp
004010C6 call __chkesp (00401280)
004010CB mov esp,ebp
004010CD pop ebp
004010CE ret
--- f:\vc\a\a.cpp ------------------------------------------------------------------------------------------
1: #include<stdio.h>
2: #include<stdlib.h>
3:
4: int sum(int _a,int _b)
5: {
00401020 push ebp 【压栈:把ebp寄存器里面的地址压到栈顶,此时esp移动到栈顶】
00401021 mov ebp,esp 【让ebp指向esp处,把esp地址赋给ebp】
00401023 sub esp,44h 【esp 指向 esp - 44h 处】
00401026 push ebx 【压栈:把ebx内容压到栈顶】
00401027 push esi 【压栈:把esi内容压到栈顶】
00401028 push edi 【压栈:把edi内容压到栈顶】
00401029 lea edi,[ebp-44h]
0040102C mov ecx,11h
00401031 mov eax,0CCCCCCCCh
00401036 rep stos dword ptr [edi] 【上述四条指令意思是:将指定区域全部初始化为CCCC形式】
6: int c=0;
00401038 mov dword ptr [ebp-4],0 【把 0 赋给 c 变量所在空间】
7: c=_a+_b;
0040103F mov eax,dword ptr [ebp+8] 【把 a 变量内容赋给 eax寄存器】
00401042 add eax,dword ptr [ebp+0Ch] 【把 b 变量内容赋给 eax寄存器做相加运算】
00401045 mov dword ptr [ebp-4],eax 【把eax寄存器 内容赋给 c 变量】
8: return c;
00401048 mov eax,dword ptr [ebp-4] 【把 c 变量内容赋给 eax寄存器】
9: }
0040104B pop edi 【出栈,esp向栈底移动一步】
0040104C pop esi 【出栈,esp向栈底移动一步】
0040104D pop ebx 【出栈,esp向栈底移动一步】
0040104E mov esp,ebp 【让esp指向ebp处,把ebp地址赋给esp】
00401050 pop ebp 【出栈,ebp向栈底移动一步,此时拿到call指令的下一条指令的地址,ebp便指向了原函数栈底】
00401051 ret
-------------------------------------------------------------------------------------------------------------------------------
函数调用过程图如下:画的不好,请多指导~