函数的调用过程,栈桢的创建和销毁。

来源:互联网 发布:2017美剧 知乎 编辑:程序博客网 时间:2024/06/05 06:58

                 函数的调用过程,栈桢的创建和销毁

我们首先要知道关于函数的调用,我们知道main函数也是要被调用的,在_tmainCRTStartup函数中调用,而_tmainCRTStartup函数是在mainCRTStartup中被调用的。

函数调用过程要为函数开辟栈空间,用于本次函数调用中临时变量的保存,称为函数栈桢

下面我用一个简单的程序说明函数的调用过程:

#include<stdio.h>#include<string>int Add(int x, int y){int sum = 0;sum = x + y;return sum;}int main(){int a = 1;int b = 2;int ret = 0;ret = Add(a, b);printf("%d", ret);system("pause");return 0;}

在这个程序里,主函数定义了3个局部变量,然后调用了Add函数。三个局部变量都在栈空间上存放。

可是当程序运行起来后,main函数是如何实现对Add函数的调用过程呢?我们转到反汇编一步步分析一下。

下面是main函数的汇编代码(在VS2013平台下):

int main(){009253D0  push        ebp  009253D1  mov         ebp,esp  009253D3  sub         esp,0E4h  009253D9  push        ebx  009253DA  push        esi  009253DB  push        edi  009253DC  lea         edi,[ebp-0E4h]  009253E2  mov         ecx,39h  009253E7  mov         eax,0CCCCCCCCh  009253EC  rep stos    dword ptr es:[edi]  int a = 1;009253EE  mov         dword ptr [a],1  int b = 2;009253F5  mov         dword ptr [b],2  int ret = 0;009253FC  mov         dword ptr [ret],0  ret = Add(a, b);00925403  mov         eax,dword ptr [b]  00925406  push        eax  00925407  mov         ecx,dword ptr [a]  0092540A  push        ecx  0092540B  call        Add (0921113h)  00925410  add         esp,8  00925413  mov         dword ptr [ret],eax  printf("%d", ret);00925416  mov         esi,esp  00925418  mov         eax,dword ptr [ret] 0092541B  push        eax  0092541C  push        92CC7Ch  00925421  call        dword ptr ds:[93019Ch]  00925427  add         esp,8  0092542A  cmp         esi,esp  0092542C  call        __RTC_CheckEsp (09212E4h)  system("pause");00925431  mov         esi,esp  00925433  push        92CD34h  00925438  call        dword ptr ds:[930184h]  0092543E  add         esp,4  00925441  cmp         esi,esp  00925443  call        __RTC_CheckEsp (09212E4h)  return 0;00925448  xor         eax,eax  }0092544A  pop         edi  }0092544B  pop         esi  0092544C  pop         ebx  0092544D  add         esp,0E4h  00925453  cmp         ebp,esp  00925455  call        __RTC_CheckEsp (09212E4h)  0092545A  mov         esp,ebp  0092545C  pop         ebp  0092545D  ret  

1、从main函数的调用开始,需要为main函数创建函数栈桢。

int main(){009253D0  push        ebp       //将ebp压入栈底009253D1  mov         ebp,esp   //将esp的值赋给ebp,得到新的ebp009253D3  sub         esp,0E4h   //将esp的值减去0E4h,得到新的esp009253D9  push        ebx        //从低地址到高地址三次压栈压入三个寄存器ebx、009253DA  push        esi       //esi、                                                                                          009253DB  push        edi       //edi009253DC  lea         edi,[ebp-0E4h]  //加载有效地址009253E2  mov         ecx,39h           //将加载到edi的地址重复拷贝ecx次,009253E7  mov         eax,0CCCCCCCCh    //即把main函数预开辟的空间全部初始化为0CCCCCCCCh009253EC  rep stos    dword ptr es:[edi]  //重复拷贝int a = 1;009253EE  mov         dword ptr [a],1   //处理局部变量a,把1赋给aint b = 2;009253F5  mov         dword ptr [b],2  // 处理局部变量b,把2赋给bint ret = 0;009253FC  mov         dword ptr [ret],0  ret = Add(a, b);00925403  mov         eax,dword ptr [b]  00925406  push        eax  00925407  mov         ecx
初始化如下,调试可知:

2、Add函数的调用,参数传递过程如下:

int ret = 0;011C540C  mov         dword ptr [ret],0   //ret的初始化ret = Add(a, b);011C5413  mov         eax,dword ptr [b]   //将b的值赋给eax,压栈eax,形参b的实例化011C5416  push        eax  011C5417  mov         ecx,dword ptr [a]    //将a的值赋给ecx,压栈ecx,形参a的实例化011C541A  push        ecx  011C541B  call        Add (011C1113h)     //调用,压栈call指令的下一条指令的地址,再跳转到Add函数的地方011C5420  add         esp,8  011C5423  mov         dword ptr [ret],eax  printf("%d", ret);

继续调试,进入Add函数:

#include<stdio.h>#include<string>int Add(int x, int y){011C3860  push        ebp             //压入ebp011C3861  mov         ebp,esp          //将esp的值赋给ebp011C3863  sub         esp,0CCh        //将esp的值减去0CCh011C3869  push        ebx  011C386A  push        esi  011C386B  push        edi  011C386C  lea         edi,[ebp-0CCh]   //加载有效地址011C3872  mov         ecx,33h  011C3877  mov         eax,0CCCCCCCCh    //初始化开辟的空间011C387C  rep stos    dword ptr es:[edi]    //重复拷贝int sum = 0;011C387E  mov         dword ptr [sum],0    //创建sum,并初始化sum = x + y;011C3885  mov         eax,dword ptr [x]     //将形参x的值赋给eax011C3888  add         eax,dword ptr [y]    //将形参y的值赋给eax011C388B  mov         dword ptr [sum],eax    //将相加后的值存储到sum中return sum;011C388E  mov         eax,dword ptr [sum]   //将sum的值赋给eax,即将结果存储到寄存器中}011C3891  pop         edi            //出栈011C3892  pop         esi            //出栈 011C3893  pop         ebx            //出栈                                                                                      011C3894  mov         esp,ebp        //将ebp的值赋给esp,使esp向下移动011C3896  pop         ebp            //出栈,将出栈的内容保存在ebp中,回到main的栈桢011C3897  ret                        //出栈一次,并将出栈的内容当做地址,将程序执行跳转到该地址处

跳转到Add函数处,



以上是简单的过程介绍,下面是栈桢创建的模拟过程图:



阅读全文
3 0
原创粉丝点击