当前位置: 首页 > ops >正文

关于 栈帧变化完整流程图(函数嵌套)

一、什么是栈帧(Stack Frame)

当一个函数被调用时,会在栈上开辟一段空间,叫做 栈帧
每个栈帧保存了:

  • 函数的参数

  • 返回地址(从哪里跳回来)

  • 上一个栈帧的栈底指针(保存调用者的 EBP / FP)

  • 局部变量

  • 保存的一些寄存器(可选)


二、函数嵌套调用例子

以 C 为例:

void C() {int c = 3;
}void B() {int b = 2;C();
}void A() {int a = 1;B();
}int main() {A();return 0;
}

三、每次函数调用的栈帧结构(以 x86 为例)

栈增长方向:从高地址向低地址

每次调用栈帧大致如下(从下到上):

高地址
│
│  上一帧的 EBP(caller 的栈底)
│  返回地址(RET)
│  参数(如有)
│  局部变量(如 int x)
│
↓
低地址

四、函数嵌套时完整栈帧流程图

以调用链 main → A → B → C 为例,假设每层函数内部有 1 个 int 局部变量。

初始状态:
栈空main() 被调用:
+-------------------+ ← ESP,EBP(main 的栈底)
| 返回地址          |
+-------------------+
| 局部变量 return=0 |
+-------------------+main() → A()
+-------------------+ ← ESP(当前)
| 返回地址(main)    |
+-------------------+
| 上一帧的 EBP      |
+-------------------+
| 局部变量 a=1      |
+-------------------+A() → B()
+-------------------+
| 返回地址(A)       |
+-------------------+
| 上一帧的 EBP      |
+-------------------+
| 局部变量 b=2      |
+-------------------+B() → C()
+-------------------+
| 返回地址(B)       |
+-------------------+
| 上一帧的 EBP      |
+-------------------+
| 局部变量 c=3      |
+-------------------+栈顶 (ESP)

五、函数返回时的栈帧回退过程

函数返回时,会弹出当前栈帧,恢复上一个函数的栈帧(EBP 和 RET 地址)。

C() return → ESP 恢复到 B()
B() return → ESP 恢复到 A()
A() return → ESP 恢复到 main()
main() return → 程序结束

六、流程图总结

main
│
├── 调用 A()
│   │
│   └── 调用 B()
│       │
│       └── 调用 C()
│
└── 每层函数进栈,栈帧不断叠加每层函数返回,栈帧依次弹出

七、可视化理解

栈顶
│
│  C 的局部变量
│  返回地址(B)
│
│  B 的局部变量
│  返回地址(A)
│
│  A 的局部变量
│  返回地址(main)
│
│  main 的局部变量
│  返回地址(操作系统)
↓
栈底

八、汇编视角分析

以 x86 为例,函数调用时常见的指令:

CALL func   ; 压入返回地址 → 跳转
PUSH EBP    ; 保存当前帧
MOV EBP, ESP; 建立新栈帧
SUB ESP, n  ; 为局部变量分配空间
...
MOV ESP, EBP; 恢复 ESP
POP EBP     ; 恢复上层栈帧
RET         ; 弹出返回地址

九、总结

步骤栈帧变化关键指令
函数调用新栈帧入栈CALLPUSH EBP
建立栈帧保存旧帧 & 分配空间MOV EBP, ESPSUB ESP, n
函数返回弹出栈帧MOV ESP, EBPPOP EBPRE

补充:

  • 在 x86_64 下,函数参数会用寄存器传递(如 RDI、RSI)

  • 在 ARM64 下,栈帧也有 FP/LR(Frame Pointer / Link Register)结构

  • 调用链跟踪调试可用 gdb、ida、ghidra 中的栈回溯(backtrace)

http://www.xdnf.cn/news/14876.html

相关文章:

  • Apache Spark 4.0:将大数据分析提升到新的水平
  • 【Linux】基础开发工具(1)
  • 【JS逆向基础】数据分析之正则表达式
  • 【java】webservice服务
  • 基于Excel的数据分析思维与分析方法
  • 【Vibe Coding 实战】我如何用 AI 把一张草图变成了能跑的应用
  • Hadoop高可用集群搭建
  • 【排坑记录】Cursor 出现 “Connection failed” 报错?试试修改 HTTP Compatibility Mode!
  • HTTPS 协议原理
  • 数据驱动实时市场动态监测:让商业决策跑赢时间
  • 操作系统王道考研习题
  • CICD[构建镜像]:构建django使用的docker镜像
  • Linux proxy设置
  • 2048小游戏实现
  • PADS交互式布局
  • 查看linux中steam游戏的兼容性
  • Python练习Day1
  • 【Elasticsearch】检索排序 分页
  • vue router 里push方法重写为什么要重绑定this
  • FLUX.1-Kontext 高效训练 LoRA:释放大语言模型定制化潜能的完整指南
  • 相机位姿估计
  • 一文讲清楚React中Refs的应用
  • 成为git砖家(12): 看懂git合并分支时冲突提示符
  • Python 机器学习核心入门与实战进阶 Day 3 - 决策树 随机森林模型实战
  • vue 条件渲染(v-if v-else-if v-else v-show)
  • 时域与频域信号特性分析——DFT归一化与双边谱合并分析
  • Kali Linux Wifi 伪造热点
  • SpringBoot:整合quartz实现定时任务-集群化配置
  • 温湿度变送器与电脑进行485通讯连接并显示在触摸屏中(mcgs)
  • Visual Studio 2022 MFC Dialog 添加Toolbar及Tips提示