堆栈在函数运行时的简单解析

来源:互联网 发布:在校大学生贷款软件 编辑:程序博客网 时间:2024/06/05 05:49

 写一个简单的求和函数简析堆栈在函数运行时所做的工作。

#define _CRT_SECURE_NO_DEPRECATE 1#include <stdio.h>    #include <stdlib.h>    int my_add(int a,int b){int sum=0;        sum=a+b;return sum;}int main(){   int a=3;   int b=2;   int ret=0;   ret=my_add(a,b);   printf("%d\n",ret);   system("pause");}

下面是mian函数对应的汇编代码

int main(){00A33D40  push        ebp  00A33D41  mov         ebp,esp  00A33D43  sub         esp,0E4h  00A33D49  push        ebx  00A33D4A  push        esi  00A33D4B  push        edi  00A33D4C  lea         edi,[ebp-0E4h]  00A33D52  mov         ecx,39h  00A33D57  mov         eax,0CCCCCCCCh  00A33D5C  rep stos    dword ptr es:[edi]     int a=3;00A33D5E  mov         dword ptr [a],3     int b=2;00A33D65  mov         dword ptr [b],2     int ret=0;00A33D6C  mov         dword ptr [ret],0     ret=my_add(a,b);00A33D73  mov         eax,dword ptr [b]  00A33D76  push        eax  00A33D77  mov         ecx,dword ptr [a]  00A33D7A  push        ecx  00A33D7B  call        _my_add (0A311F9h)  00A33D80  add         esp,8  00A33D83  mov         dword ptr [ret],eax     printf("%d\n",ret);


      首先进入main函数:main函数是由_ _mainCRTstartup的函数调用的所以首先在栈里給这个函数划分一个区域

 
然后看汇编的第一句:push ebp   (压栈  把ebp压入栈顶,同时esp向上移动)

 
接下来 mov         ebp,esp      (把esp的值給ebp,  即把ebp向上移动到esp的位置,这时esp,ebp指向同一块区域)


 接下来 sub         esp,0E4h  (esp地址减去0x0E4h,即把esp低地址(向上)方向移动0E4h个内存单位)


 接下来 push        ebx  
             push        esi  
             push        edi       (压栈)



 接下来 :
lea         edi,[ebp-0E4h]  (加载有效地址,把ebp减去0E4h的值給edi,最直观的就是edi指向了exb接下来的空间)


 接下来:   mov         ecx,39h  
                  mov         eax,0CCCCCCCCh  
                  rep stos    dword ptr es:[edi]     (把39h給ecx,把0CCCCCCCCh 給eax,然后重复的做一件事,即从edi指向的地方开始把0CCCCCCCCh赋給接下来的地址空    



 我们可以看看内存中的现象0x010FFD30即为edi指向。可以看出从edi后的39h个单元都被初始化位0xcccc


 接下来:
 int a=3;
 mov         dword ptr [a],3  
 int b=2;
 mov         dword ptr [b],2  
 int ret=0;
 mov         dword ptr [ret],0    ( 先把a放进栈里面,再把b放进栈里面最后把ret放进栈里面,栈时从高地址作用到低地址,先进后出,先进的在最底下)


 我们再来看看内存里面的现象:


 接下来:  ret=my_add(a,b);
                 mov         eax,dword ptr [b]  
                 push        eax  
                 mov         ecx,dword ptr [a]  
                 push        ecx                                       (把b中存储的数給eax,然后eax压栈,把a中存储的数給ecx,然后ecx压栈 ,这是两个寄存器,这其实就是形参的拷贝)



 接下来 :call        _my_add (011311F9h)  
                  (执行到这一步,就会跳转进入我们写的my_ad这个子函数)


 接下来:
01131470  push        ebp  01131471  mov         ebp,esp  01131473  sub         esp,0C0h  01131479  push        ebx  0113147A  push        esi  0113147B  push        edi  0113147C  lea         edi,[ebp-0C0h]  01131482  mov         ecx,30h  01131487  mov         eax,0CCCCCCCCh  0113148C  rep stos    dword ptr es:[edi]  

        我们在mian函数的上面再开辟一片空间,接下来这些步骤跟我们进入主函数时所做的开始工作时一模一样的从压栈ebp到0CCCCCCCCh  的初始化完成。  (图一直是从下到上连续的,截屏有限,最底下的截不上)


 接下来:
             int  sum=0;


 我们来看看 内存中的变化:


   
       第二次次初始化完成后我们发现第二次初始化后的结束空间很接近第一次初始化的开始时空间,这直接证明了每一次开辟新的栈都是在现有的栈上面的空间开辟,和我们图从底下开始画,像修高楼一样,越来越高。也直接证明了栈的作用方向是从高到低,从栈底开始,向栈顶作用。(栈底是高地址,栈顶是低地址)。

这是my_add下半段汇编代码:


 接下来:
 
     sum=a+b;
     mov         eax,dword ptr [a]  
     add         eax,dword ptr [b]  
     mov         dword ptr [sum],eax     (把a的值給eax,eax的值加上b赋值給eax,最后把eax的值赋給sum,其实就是把a+b赋給sum,所以执行完sum应该变为5)


   
 我们来看看内存中的变化:


 接下来
return sum;
mov         eax,dword ptr [sum]    (把sum的值給eax那么现在eax中的值就是5.)
接下来
   pop         edi  
   pop         esi  
   pop         ebx                                          (edi,esi,ebx,出栈)


  
接下来:
mov         esp,ebp          (把ebp的值赋給esp,也就是把esp向下(高地址)移动,放弃之前所创建的my_add的栈区)


  
 接下来: pop         ebp    (ebp出栈,ebp回到main函数最开始的ebp处)
(my_add已经销毁)



 接下来执行 : ret  (直接跳转,跳转回了主函数main里面)



 接下来执行  add   esp,8   (esp向下移动两个单位,销毁两个形参)


接下来执行
mov         dword ptr [ret],eax        (把eax的值交给ret,我们之前在子函数my_add最后把sum的值給了eax这个寄存器,而sum的值是形参3+2的值也就是5,从而这句话的意义就是把计算结果5赋值給ret)
我们来看内存中ret的值:


 接下来:
 printf("%d\n",ret);      (相信这是最熟悉的一步了输出ret,程序执行到这一步我们逻辑步骤已经走完了)


剩下的都是像之前的移动esp,ebp来销毁栈,还包括一些系统的函数调用,这并不是重点,所以就不一一解释了。

       最终达到esp和ebp都指向栈底时即所有栈空间都销毁完成,程序彻底结束。
       我们可以注意到堆栈的创建是一个相反的过程,就好像时我们从1楼爬楼梯(创建堆栈),到了楼顶,做了该做的事情,然后又从楼梯下楼(销毁堆栈)。函数的功能就是在这创建堆栈和销毁堆栈的过程中实现的。



0 0
原创粉丝点击