堆栈操作深度解析:PUSH/POP 与函数调用的秘密

堆栈是程序运行的基石。从 PUSH/POP 指令到栈帧结构,再到函数调用时的参数传递与返回地址保存,一文彻底搞懂栈在汇编中的底层运作机制。

2

栈是什么?

栈(Stack)是一种 后进先出(LIFO) 的数据结构。在汇编中,它由 SS:SP 指向。

PUSH 与 POP

1
2
PUSH AX    ; SP = SP - 2, [SS:SP] = AX
POP  BX    ; BX = [SS:SP], SP = SP + 2
  • PUSH:先将 SP 减 2,再写入数据
  • POP:先读取数据,再将 SP 加 2

栈帧结构

函数调用时,栈中会形成栈帧

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
高地址
  +----------------+
  |    参数 2      |
  +----------------+
  |    参数 1      |
  +----------------+
  |  返回地址      | ← CALL 自动压入
  +----------------+
  |  旧 BP         | ← PUSH BP
  +----------------+ ← BP (当前栈帧基址)
  |  局部变量 1    |
  +----------------+
  |  局部变量 2    |
  +----------------+ ← SP (栈顶)
低地址

函数调用流程

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
; 调用者
PUSH 参数 2
PUSH 参数 1
CALL FUNC

; 被调用者 FUNC:
PUSH BP        ; 保存旧栈帧
MOV BP, SP     ; 设置新栈帧
SUB SP, 4      ; 分配局部变量空间

; ... 函数体 ...

MOV SP, BP     ; 恢复 SP
POP BP         ; 恢复旧栈帧
RET            ; 弹出返回地址并跳转

为什么需要栈?

  1. 保存返回地址CALL 自动将下一条指令地址压栈
  2. 传递参数:通过栈传递,避免寄存器不够用
  3. 保存上下文:子程序可以随意使用寄存器,退出前恢复
  4. 局部变量:每个函数有独立的栈帧,互不干扰

总结

理解栈,就理解了程序执行的"时间线"。每一次函数调用,都是在栈上翻开新的一页。

下一篇:《标志位详解:CF/ZF/SF/OF 如何影响程序流程》

广告
广告位预留中 (728x90)