BoKe

萌新博客

函数堆栈(函数栈帧)

函数的调用过程

每一次函数的调用都是一个过程,这个过程我们称之为函数的调用过程。这个过程需要为函数开辟栈空间,用于函数调用中临时变量的保存,保护。这块栈空间我们称之为函数栈帧

先写一段代码:

#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 = 0;
    ret = Add(a,b);
    printf("ret = %d\n",ret);
    return 0;
}

在调用main函数之前

  • 先按F10进行逐过程调试
    这里写图片描述
    可以看到在调用主函数main之前,还调用了
    __tmainCRTStartup()函数 和 mainCRTStartup()函数。
  • 然后我们对准主函数main右键,转到反汇编。
    这里写图片描述
    可以观察到先是移动ebp、esp两个寄存器。
    ebp-保存堆栈底部的地址
    esp-保存堆栈顶部的地址
    在调用main函数之前,__tmainCRTStartup()函数的堆栈
    这里写图片描述
  • 在反汇编里继续往下走
    堆栈从下往上是由高地址到低地址,函数一步步往后走,堆栈是逐步往上面压栈。
    这里写图片描述
    这里写图片描述
    注:每个方块都为4个字节
    1、在__tmainCRTStartup()上压一个栈,移动esp,ebp。
    2、为main函数预开辟一个E4h大小的空间。
    3、在预开辟空间上压三个栈ebx,esi,edi(先不做介绍,因为到最后这三个栈会被原封弹出栈空间)。
    4、将为main开辟的空间初始化为CCCCCCCC。

开始执行main函数

  • 定义变量赋值
    这里写图片描述
    1、创建a,放在[ebp-8h],从ebp开始向上数第8个字节的空间用来存放a的值0Ah。
    2、创建n,放在[ebp-14h],从ebp开始向上数第20个字节的空间来存放b的值14h。
    3,、创建ret,放在[ebp-20h],从ebp开始向上数第32个字节的空间来存在ret的值0。
  • 准备给函数传参
    这里写图片描述
    1、将[ebp-14h](也就是b)赋值给eax,并把eax压栈。
    2、将[ebp-8](也就是a)赋值给ecx,并且压栈。
    3、开始调用函数(记住call前面的地址),当F10走到这里时,按F11进入函数,然后继续F10。
    这里写图片描述
    此时,又会在栈顶部压一个地址,用来存放调用函数的地址(以便用完函数可以准确的跳回主函数main)
    这里写图片描述

函数的调用

  • 跟main函数一样,Add函数会在栈内开辟一部分空间
    这里写图片描述
    1、将main函数的ebp保存起来压栈(以便调用完Add函数后可以返回到main函数的底部)。
    2、将新的ebp放在栈顶(Add函数堆栈的底部)
    3、为Add函数预开辟一片空间大小为CCh字节
    4、在顶部继续压三个栈ebx,esi,edi。
    最后将为Add开辟的CCh字节大小的空间全部初始化为全C。
  • 开始执行函数体
    这里写图片描述
    1、定义z,[ebp-8]即ebp向上数第8个字节处空间为z=0。
    2、将[ebp+8]也就是ebp向下数第8个字节处,也就是a的值20,赋值给寄存器eax。
    3、将[ebp+0Ch]也就是ebp向下数第12个字节处,也就是b的值加到eax里,也就是将10和20加起来放到寄存器eax中。
    4、将eax赋值给[ebp-8],也就是z。
    5、将z的值赋值给寄存器eax(将z的值保存起来)。

开始销毁函数体

这里写图片描述
1、弹出edi,esi,ebx三个压栈。
2、将esp移动到ebp位置,此时Add函数直接全部弹出(调用完毕)
3、弹出ebp,因为当初保存的main函数的ebp,所以此时ebp返回到main底部。ret弹出刚才保存的调用函数的地址,main下面的栈空间。
Add函数完成销毁。
新的esp、ebp如下:
这里写图片描述

回到主函数main

这里写图片描述
1、esp加8,esp向下移8个字节。也就是将ecx,eax弹出。即a跟b的临时拷贝(形参)移出。
2、将Add函数内部保存的eax的值赋给[ebp-20],也就是将Add函数计算好的值返回给ret。
结果ret计算完成。
- 下面放一个整体的图
这里写图片描述
函数栈帧部分内容在不同编译器上出现的结果不同,但是思想都是一致的。小弟用vs2008献丑一把,望大神指导。

阅读更多
文章标签: 函数栈帧
上一篇编程实现扫雷小游戏
下一篇简析可变参数,模拟实现printf函数
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭