【c语言】函数栈帧

1.函数栈帧的基本概念

在调用函数时,为函数开辟栈空间,用于本次函数调用中临时变量的保存与现场保护等等。栈是由高地址向低地址延伸的。寄存器ebp指向当前的栈帧的地步(高地址),寄存器esp指向当前栈帧的顶部(低地址)。

2.实例分析

  • 代码
 #include<stdio.h>

    int Add(int x, int y)
    {
        int z = 0;
        z = x + y;
        return z;
    }
    int main()
    {
        int a = 10;
        int b = 20;
        int ret = Add(a, b);
        printf("ret=%d", ret);
        return 0;
    }

调试模式下使用调用堆栈功能,我们可以发现main函数在调用之前是在函数—_mainCRTStartup中调用,而_mainCRTStartup又是在mainCRTStartup函数中调用。

  • main函数的栈帧
    在这里插入图片描述
  • 解析函数栈帧
    1.main函数:
    在这里插入图片描述
  • 首先是_mainCRTStartup()函数的调用,然后在调用main函数;
  • 将ebp做压栈处理,保存指向栈底的地址(方便函数返回之后的现场恢复)
  • 将esp的值赋给ebp,产生新的ebp;
  • 给esp减去一个16进制数0E4H(为main函数预开辟空间)
  • 将ebx,esi,edi做压栈处理,对应蓝色部分
  • lea指令,加载有效地址,初始化预开辟的空间0xcccccccc,对应橙色部分
  • 变量a与变量b的创建
    Add函数的调用过程:
    在这里插入图片描述
  • 将实参b存入寄存器eax,再将eax做压栈处理;(传参过程,从右向左传递,先传b)
  • 将a存入寄存器ecx,再将ecx压栈;
  • call指令的调用,先将call指令下一条指令的地址压栈,然后跳转(jmp)到Add()函数的地方。

2.进入Add函数
在这里插入图片描述

  • 首先将main()函数ebp压栈处理,保存指向main()函数栈帧底部的地址(方便函数返回之后的现场恢复),此时esp指向新的栈顶位置
  • 将esp的值赋给ebp,产生新的ebp,即Add()函数栈帧的ebp;
  • 给esp减去一个16进制数0E4H(为Add()函数预开辟空间)
  • 将ebx,esi,edi做压栈处理
  • lea指令,加载有效地址
  • 初始化预开辟的空间为0xcccccccc
  • 变量z的创建
  • 获取形参的a和b再相加,将结果存储在z中;
  • 将结果存储到eax寄存器,通过寄存器带回函数的返回值

3.Add函数返回
在这里插入图片描述

  • edi,esi,ebx依次出栈,esp向下移动;
  • 将ebx赋给esp,使esp指向edp指向的地方
  • ebp出栈,将出栈的内容给ebp(即main-ebp),回到main(函数的栈帧)
  • ret指令,出栈依次,并将出栈的内容当做地址,并跳转到该地址处(call指令下啊一条执行)

猜你喜欢

转载自blog.csdn.net/weixin_41892460/article/details/86775333