关于函数的调用过程(栈帧)

来源:互联网 发布:光学透镜设计软件 编辑:程序博客网 时间:2024/05/18 03:08

    以以下函数为例,来了解函数的调用过程,即栈帧。

#include <stdio.h>#include <stdlib.h>int Add(int x, int y){int ret = 0;ret = x + y;return ret;}int main(){int a = 10;int b = 20;int ret = 0;ret = Add(a, b);printf("%d\n", ret);system("pause");return 0;}
    在vs2013中调试该程序,并在调试窗口转汇编,可以得到该程序的汇编语言。

    1、在正式看汇编语言之前,我们需要先了解一下main()函数的调用。 main()函数被__tmainCRTStartup()函数调用,__tmainCRTStartup()函数被mainCRTStartup()函数调用。  所以,先为mainCRTStartup()函数开辟空间,再为__tmainCRTStartup()函数开辟空间,最后为main函数开辟空间。

 【注意】1)很显然,本文中的图像都是上为高地址,下为低地址

      2)本文中省略了很多esp的变化过程,但读者需知道,esp始终指向栈顶(出栈操作中esp会改变,但仍然只指向栈顶,被修改的部分已不在栈内)

    

    2、进入main函数的汇编语言

      保留__tmainCRTStartup()的栈底,因为运行完main()后还要回到__tmainCRTStartup()

         改变ebp的位置



   main开辟空间,0E4h大小

 


   压栈ebx、esi、edi

 


   加载有效地址 使edi指向ebp-0E4h处

 


   39h = 0E4h/4  39h存入ecx寄存器。四个字节为一个单位,共39h个单位

   将随机数存入eax寄存器

   从edi指向的位置向下重复赋值,一次2个字节,赋为随机值。即将栈帧开辟的空间全部初始化为随机值

 


   

   


   

   


   

   


   

  将b的值放入eax寄存器中,然后将其压栈;将a的值放入ecx寄存器中,然后将其压栈。
  用_b表示b的值,用_a表示a的值,该过程在内存中的表示如下:

 


   调用Add函数,压栈012D3F20(即 call指令下一条指令的地址)

   暂时不用看这两条指令,等Add函数调用完会返回此处

 


 3、进入Add()函数的汇编语言

 

 与上述过程类似,此过程在内存中的变化为:

 


 

 // 将main函数空间中存储的_a的值(形参)存入eax寄存器,此时eax中的值为10
 // 将main函数空间中存储的_b的值(形参)加入eax寄存器,此时eax中的值为30
 // 将eax寄存器中的值(30)存入Add函数中ret局部变量中
 // 将ret局部变量的值(30)存入寄存器eax中,作为返回值


   

 // 这段代码是为了销毁Add函数空间,首先edi、esi、ebx出栈
 // 然后将ebp的值给esp,过程如下:

 

     //然后将ebp指回main函数的ebp,销毁存储main函数ebp的空间

 


 // 此时回到main函数中call指令的下一条指令处:

 

 // 第一条指令销毁形参,将_a、_b空间销毁
 // 第二条指令将eax寄存器中存储的ret返回值(30)存入main函数空间的ret中


 至此,Add函数已全部结束,main函数也基本结束,对于printf和system(“pause”),以及return 0就不再多做解释。

 全部过程整理到一张图上为:

 

原创粉丝点击