news 2026/10/13 2:24:20

【C语言深度解剖】函数栈帧的创建与销毁(超详细·图文版·适合面试/底层原理)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【C语言深度解剖】函数栈帧的创建与销毁(超详细·图文版·适合面试/底层原理)

前言:
大家好,本篇文章带你彻底吃透 C 语言函数调用的底层:函数栈帧(Stack Frame)的创建、维护、参数传递、返回值、销毁全过程。
内容偏底层硬核,适合想搞懂:

  • 函数为什么能调用?

  • 局部变量为什么不能跨函数使用?

  • 栈溢出是什么?

  • 指针越界、野指针根源在哪?

  • 面试常问:函数调用栈原理

看完这篇,你对 C 语言的理解会直接上一个台阶。

一、前置知识(必须先懂)

  1. 什么是栈(stack)
  • 栈是内存中一块专门用于函数调用的区域

  • 生长方向:从高地址 → 低地址(向下生长)

  • 遵循:后进先出 LIFO(最后调用的函数最先返回)

  1. 两个关键寄存器(理解栈帧核心)
  • ebp / rbp:栈底基址指针,固定不动,标记当前函数栈帧底部

  • esp / rsp:栈顶指针,一直在变,指向栈最顶端

  • 栈帧 = ebp 到 esp 之间的这片空间

  1. 关键指令
  • push :把数据压入栈,esp 减小(向低地址走)

  • pop :把数据从栈取出,esp 增大(向高地址走)

  • mov :数据赋值

  • call :调用函数,会把下一条指令地址压栈

  • ret :函数返回,从栈中弹出返回地址

二、环境与测试代码

我使用:

  • Windows VS2022

  • x86(32位,更容易观察)

  • 调试 + 反汇编

    功能:
    main 调用 Add ,计算 a + b,返回结果并打印。

三、栈帧创建与销毁 完整逐步骤讲解

第一步:进入 main 函数

任何函数执行前,都要先创建栈帧。

  1. 保存上一个函数的 ebp
  • 将调用 main 的函数的栈底指针压入栈

  • esp -= 4

  1. 让 ebp = esp,确立新栈帧
  • 现在 ebp 就是当前栈帧的底部

  • 从这一刻起,ebp 不再动

  1. 为局部变量预留空间
  • esp 向下移动,留出局部变量空间

  • 栈帧大小确定

第二步:main 函数局部变量创建

对应汇编类似:

  • ebp-4 → 变量 a

  • ebp-8 → 变量 b

它们都在 main 的栈帧内部。

第三步:准备调用 Add 函数(参数压栈)

执行:

重点:C 语言参数压栈顺序:从右向左

  1. 先压 b

  2. 再压 a

    每次 push,esp 都在减小。

然后 call 调用函数

call 做两件事:

  1. 把下一条指令地址压栈(返回地址)

  2. 跳转到 Add 函数

第四步:Add 函数栈帧创建

进入 Add 后,和 main 一模一样的流程:

  1. 保存 main 的 ebp
  2. 新栈帧:ebp = esp
  3. 为 Add 的局部变量 z 开辟空间

    现在 Add 栈帧形成:
  • ebp:栈底

  • esp:栈顶

第五步:Add 函数内部执行

x、y 在哪?
在栈帧上方,ebp 往上的位置:

  • [ebp+8] → x(a 的值)

  • [ebp+12] → y(b 的值)

执行:

  • ebp-4 → z

  • z = x + y 完成

第六步:函数返回(return z)

返回值不会通过栈返回,而是通过 eax 寄存器:

第七步:Add 栈帧销毁(核心!)

  1. 回收局部变量空间
  • esp 回到 ebp 位置

  • 局部变量 z 所在空间直接被放弃

  • 数据还在,但无效了

  1. 恢复 ebp(回到 main 的栈底)
  • 弹出之前保存的 ebp

  • ebp 重新指向 main 的栈底

  1. ret 返回 main
  • 从栈中弹出 call 保存的返回地址

  • 跳回 main 继续执行

第八步:回到 main,清理参数

  • 之前压入的两个参数 a、b 占用 8 字节

  • 直接移动 esp 销毁它们

第九步:main 继续执行

eax 中的返回值存入 ret 变量。

四、一张图总结整个流程(文字版示意图)

五、最核心结论(面试必背)

  1. 每调用一个函数,就创建一个独立栈帧

  2. ebp 固定栈底,esp 动态栈顶

  3. 栈从高地址向低地址生长

  4. 局部变量在栈帧内,函数结束即销毁

  5. 销毁不是清零,是esp 移动,使用权回收

  6. 参数压栈顺序:从右向左

  7. 返回值通过 eax 寄存器 带回

  8. 栈帧层层嵌套,形成 函数调用栈

六、结语:

函数栈帧是 C 语言进阶必过的一道坎。
搞懂它,你看代码就不再是“表面文字”,而是内存里的真实运行逻辑。

如果你觉得这篇文章对你有帮助,别忘了 点赞 👍 收藏 ⭐ 关注 📢

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

C语言分支和循环语句详解

在C语言中,程序的执行流程并非总是按顺序线性推进。分支语句的核心作用,就是让程序能够根据不同的条件,选择执行不同的代码块,从而实现灵活的逻辑判断。一、if 语句if 是最基础的分支判断工具,它通过检查一个条件表达式…

作者头像 李华
网站建设 2026/10/13 2:22:11

告别反复改选择器:Scrapling 爬虫指南

告别反复改选择器:Scrapling 爬虫指南 【免费下载链接】Scrapling 🕷️ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl! Dont be shy, join here: https://discord.gg/EMgGbDceNQ and follo…

作者头像 李华
网站建设 2026/10/13 2:21:27

RISC-V IDE MRS使用笔记(十四):用户反馈功能

RISC-V IDE MRS使用笔记(十四):用户反馈功能 今天给大家带来MRS用户反馈功能的分享,开发者们可以通过该功能向MRS技术支持提交:MRS相关操作咨询、改进建议或嵌入式开发中遇到的问题,用户也可以将上述内容直…

作者头像 李华
网站建设 2026/10/13 2:19:34

用Excel制作标准波士顿矩阵图:散点图与误差线实战教程

Excel波士顿矩阵图一直是商业分析里的高频需求,几乎每份战略汇报PPT里都会出现一张。但真正能用手头Excel做出来的人,说实话不多——大多数教程要么只讲原理不讲操作,要么上来就让你装各种插件,最离谱的是直接让你画个四宫格填颜色…

作者头像 李华