news 2026/10/16 3:15:18

函数栈帧的创建和销毁

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
函数栈帧的创建和销毁

文章目录

  • 一、什么是函数栈帧?
  • 二、理解函数栈帧能解决什么问题?
  • 三、函数栈帧的创建和销毁解析
    • 3.1什么是栈?
    • 3.2认识相关寄存器和相关指令
    • 3.3解析函数栈帧的创建和销毁
      • 3.3.1`main`函数栈帧的创建准备
      • 3.3.2创建`main`函数栈帧:
      • 3.3.3`main`函数栈帧中创建局部变量
      • 3.3.4传参
      • 3.3.5创建`Add`函数栈帧
      • 3.3.6`Add`函数栈帧中创建局部变量
      • 3.3.7返回`z`
      • 3.3.8销毁`Add`函数栈帧
      • 3.3.9`main`获得`Add`函数返回的值

一、什么是函数栈帧?

函数栈帧(stack frame)就是函数调用过程中在程序的调用栈(call stack)所开辟的空间,这些空间是用来存放:

  • 函数参数和函数返回值
  • 临时变量(包括函数的非静态的局部变量以及编译器生产的其它临时变量)
  • 保存上下文信息(包括在函数调用前后需要保持不变的寄存器)

二、理解函数栈帧能解决什么问题?

  • 局部变量是如何创建的?
  • 为什么局部变量不初始化内容是随机的?
  • 函数调用时参数是如何传递的?传参的顺序是怎样的?
  • 函数的形参和实参是如何示例化的?
  • 函数的返回值是如何带回的?

三、函数栈帧的创建和销毁解析

3.1什么是栈?

在经典的计算机科学中,栈是一种特殊的容器,遵循先入先出(First in First out, FIFO)。
在计算机系统中,栈则是一个具有FIFO属性的动态内存区域。程序可以将数据压入栈中,使得栈增大;也可以将数据从栈顶弹出,而弹出操作使得栈减小。栈总是向下增长(由高地址向低地址的)。

3.2认识相关寄存器和相关指令

相关寄存器
eax:通用寄存器,保留临时数据,常用于返回值
ebx:通用寄存器,保留临时数据
eip:指令寄存器,保存当前指令的下一条地址
esp:栈顶寄存器
ebp:栈底寄存器

3.3解析函数栈帧的创建和销毁

声明:
下面的图中画的格子数并不代表编译器具体分配的空间大小,只是为了方便理解,我随意画的

每一次函数调用,都要为本次函数调用开辟一块空间,就是函数栈帧的空间。开辟的空间遵循FIFO。
每一个函数开辟的空间是使用寄存器esp和ebp维护的,ebp记录栈底的地址,esp记录栈顶的地址。

以下面的代码为例,来介绍函数栈帧的创建和销毁:

#include<stdio.h>intAdd(intx,inty){intz=0;z=x+y;returnz;}intmain(){inta=3;intb=5;intret=0;ret=Add(a,b);printf("%d\n",ret);return0;}

3.3.1main函数栈帧的创建准备

我们都知道程序都是从main开始运行的,但是main也是被别的函数调用的。mainCRTStartup函数调用_tmainCRTStartup函数,然后_tmainCRTStartup函数调用main函数。

3.3.2创建main函数栈帧:

首先将ebp压入_tmainCRTStartup函数栈帧中,然后esp的值减小(栈中每压入一个元素,栈都要增大,栈顶改变,指向栈顶的指针就要改变,栈顶是低地址,栈底是高地址),接着将esp的值赋值给ebp,
再然后esp的值减小(减多少具体看实际要用到的空间),为main函数预分配空间

依次将ebx、esi、edi压栈
上面三个寄存器在函数随后执行中可能会被修改,所以先保存寄存器原来的值,以便在退出函数时恢复

将事先为main栈帧预开辟的空间填充为0xcccccccc

3.3.3main函数栈帧中创建局部变量


每个局部变量之间间隔多大空间是根据编译器定的
变量如果不初始化,对应的值就是0xcccccccc(对应的字符是烫),这就是为什么屏幕会打印烫烫烫烫烫烫。

3.3.4传参

将a和b的值压栈,将操作指令的下一条指令的地址压入栈中,方便后续返回main函数中继续操作

3.3.5创建Add函数栈帧

将main栈底地址压入main函数栈帧中,接着将esp的值给ebp,然后esp的值减小,为Add函数调用分配内存,最后依次将ebx、esi、edi压入栈中。

同main函数一样,将分配的栈帧空间填充为0xcccccccc

3.3.6Add函数栈帧中创建局部变量

创建局部变量z

计算a + b的值,赋值给z:将a的形参加到eax中,再将b的形参也加到eax中,此时eax的值就是8,最后将eax的值赋值给z


所以说函数的形参是实参的一份拷贝

3.3.7返回z

将z的值给了eax,在返回到main中调用eax就可以了
解决了函数结束之后,局部变量z也随之销毁,导致main函数无法得到返回值的问题

3.3.8销毁Add函数栈帧

依次弹出edi、esi、ebx,将此时ebp的值给了esp,ebp的值变为main函数栈帧栈底的地址(在创建Add函数栈帧前已经存过),此时ebp和esp分别指向main函数栈帧的栈底和栈顶,Add函数栈帧已经销毁

3.3.9main获得Add函数返回的值

在返回main后,该如何继续执行操作呢?
这时就会用到之前存放操作指令的下一条指令的地址,根据这个地址的指令执行后面的操作,弹出a和b的形参
将eax(之前将Add函数返回值暂存与eax中)的值给了变量ret


以上仅仅是我个人的粗浅理解,只描述了大概的过程,很多细节都没有涉及。我希望通过我的文章,您对二、理解函数栈帧能解决什么问题?有了大概的理解。同时,也欢迎各位大佬的点评。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/16 3:14:22

WPS被微软坑了20年,与Office恩怨未了,神秘新型表格悄然崛起?

在办公软件界&#xff0c;WPS和微软office绝对是响当当的两大巨头&#xff0c;它们之间的故事&#xff0c;犹如一部跌宕起伏的商业传奇。回溯到1988年&#xff0c;求伯君在深圳历时1年4个月&#xff0c;凭借着超强的毅力和技术实力&#xff0c;写出了12万多行代码&#xff0c;成…

作者头像 李华
网站建设 2026/10/16 3:14:22

iPhone Duo 介绍

iPhone Duo 产品介绍 苹果首款折叠屏 iPhone&#xff1a;合上是手机&#xff0c;展开是口袋里的工作区 文档版本&#xff1a;2026 年 10 月 资料来源&#xff1a;Apple 官方新闻稿与产品技术规格页&#xff0c;以及 2026 年 9 月 10 日秋季发布会的公开报道 一分钟速读 形态&am…

作者头像 李华
网站建设 2026/10/16 3:13:32

Arduino 28BYJ-48步进电机抖动不转与反向故障排查指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/16 3:12:22

从“老头乐“到主桌:Step 5 Preview 的逆袭叙事是怎么来的

从"老头乐"到主桌&#xff1a;Step 5 Preview 的逆袭叙事是怎么来的 【免费下载链接】Step-5-Preview Step 5 Preview 是阶跃星辰面向智能体工作打造的旗舰模型。该模型在软件工程和专业知识工作领域均展现出前沿级性能&#xff0c;尤其在金融领域表现突出。 Step 5 …

作者头像 李华
网站建设 2026/10/16 3:12:15

从统一响应到 Swagger:一篇讲清微服务接口文档设计

接口文档不是“写给别人看的说明书”&#xff0c;而是前后端协作、错误处理和接口测试的共同依据。本文将梳理接口文档的组成、统一响应格式、状态码设计&#xff0c;以及如何使用 Swagger 自动生成接口文档。一、接口文档解决什么问题&#xff1f; 在微服务项目中&#xff0c;…

作者头像 李华