函数的堆栈调用

函数堆栈调用的步骤:

1.开辟形参内存并进行初始化;

2.压入下一行指令的地址;

3.压入调用方栈底指针的值;

4.开辟全局变量所需的栈空间并初始化。

堆栈的定义:

       编译器一般使用堆栈实现函数调用。堆栈是存储器的一个区域,嵌入式环境有时需要程序员自己定义一个数组作为堆栈。Windows为每个线程自动维护一个堆栈,堆栈的大小可以设置。编译器使用堆栈来堆放每个函数的参数、局部变量等信息。

栈上的两个指针:

      在栈上开辟内存后,在栈上访问变量和数据都是通过访问ebp指针的偏移量来实现。一个栈用两个指针来表示,其中ebp为栈底指针,esp为栈顶指针。从下往上看,栈底是高地址,栈顶是低地址。

堆栈相关的寄存器:

ebp:栈底指针寄存器---------->指向系统栈最上面一个栈帧的底部;

esp:栈顶指针寄存器---------->指向系统栈最上面一个栈帧的栈顶;

下面通过实例来进行函数堆栈的调用过程:

int  sum(int a,int  b)

{

    int tmp = 0;

    tmp = a+b;

    return tmp;

}

int  main()

{

    int a = 10;

    int b = 20;

    int  prt=0;

    prt = sum(a,b);

    printf("prt = %d\n",prt);

}

下面描述具体的调用过程:

一、压栈:

       调用方:(每次压栈esp都向上移动)

       1.将形参变量的地址和值压栈:由下向上将 a =10 ,b = 20 ,ret = 0 依次压入函数栈帧。

       2.将实参压栈:从反汇编中可以看出先将 b的值  20  放到eax寄存器中,然后eax入栈,再将a的值 10 放到ecx寄存器中,然后ecx入栈。所以形参是在调用方开辟内存。在调用函数时,实参是从右向左入栈,这样做的目的是可以确定参数的个数和大小。

       3.调用 call 指令,跳转到下一行指令的函数,将下一行指令的地址入栈。

        被调用方:

       1.ebp压栈:将调用方函数的栈底地址记录到被调用方函数栈底中(上图中我们假设地址为0X100),为了方便清栈时esp回退到调用方栈底。将esp 赋给 ebp 。从上图中我们可以看到,紫色的框是调用方函数的栈帧。ebp压栈的内存就是被调用方函数的栈底。

       2.返回prt:将a = 10 和 b = 20 相加赋给prt。通过eax寄存器将prt的值带回.

二、清栈:

       1.在调用方调用完sum函数后,系统会对被调用方的地址空间进行回收,进行清栈操作,最后对调用方函数进行清栈,整个程序运行结束。ebp栈底指针又重新指向栈的底部。

       从程序开始运行申请开辟地址空间,到程序运行结束进行清栈操作,这是次完整的函数堆栈调用过程。

小结:

       1.形参在哪里开辟内存?

           形参所需的内存由调用方进行开辟;

       2.形参的入栈顺序?

           形参入栈顺序为由右向左进行入栈,这样能够确定参数的个数以及大小;

        3.sum 函数如何回退到main 函数?

            一般函数的返回值由寄存器带出,当返回值为4个字节时,由eax寄存器带出;为8个字节时,由eax和edx寄存器带出;大于8个字节时,返回值由临时变量带出,临时变量空间在调用方栈上开辟,一个eax寄存器中保存临时变量的地址。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值