目录
[参数 局部变量 esp ebp的关联说明](#参数 局部变量 esp ebp的关联说明)
[func 函数执行期间](#func 函数执行期间)
[EBP 的核心作用:栈帧基址锚点](#EBP 的核心作用:栈帧基址锚点)
[参数访问:EBP + 正偏移(在 EBP 上方)](#参数访问:EBP + 正偏移(在 EBP 上方))
[局部变量访问:EBP - 负偏移(在 EBP 下方)](#局部变量访问:EBP - 负偏移(在 EBP 下方))
[ESP 的动态变化](#ESP 的动态变化)

C代码与汇编映射
cpp
int func(int a, int b) {
int x = a + 1; // 局部变量x
int y = b * 2; // 局部变量y
return x + y;
}
int main() {
int result = func(20, 42);
return result;
}
-
参数压栈顺序:
- 从右向左(先压42,再压20),确保参数a在低地址,b在高地址;
-
栈帧建立:
- 通过push ebp与mov ebp, esp构建当前函数的栈帧基址;
-
局部变量分配:
- sub esp, 8在EBP下方预留8字节空间(x与y各4字节)。
汇编流程
cpp
; === main 函数(调用者) ===
main:
push ebp ; 保存main的旧EBP(上下文保护)
mov ebp, esp ; 建立main的栈帧
sub esp, 16 ; 为局部变量分配16字节空间(含对齐)
; 压栈参数(从右向左):
push 42 ; 参数2: b = 42(先压入,位于高地址)
push 20 ; 参数1: a = 20(后压入,位于低地址)
call func ; 压入返回地址(EIP),跳转到func
add esp, 8 ; 清理参数(cdecl约定由调用者清理)
mov DWORD PTR [ebp-4], eax ; 保存返回值到result
mov eax, 0 ; 返回0
leave ; 等价于: mov esp, ebp; pop ebp
ret
; === func 函数(被调用者) ===
func:
push ebp ; 保存调用者(main)的EBP(关键!形成EBP链)
mov ebp, esp ; 设置当前栈帧基址(此时EBP指向旧EBP位置)
sub esp, 8 ; 分配8字节局部变量空间(x和y各4字节)
; --- 栈帧结构关键点 ---
; [ebp] = 保存的旧EBP(main的EBP)
; [ebp+4] = 返回地址(main中call后的下一条指令)
; [ebp+8] = 第1个参数 a = 20
; [ebp+12] = 第2个参数 b = 42
; [ebp-4] = 局部变量 x
; [ebp-8] = 局部变量 y
; 访问参数并计算局部变量:
mov eax, DWORD PTR [ebp+8] ; eax = a (20)
add eax, 1 ; eax = a+1 (21)
mov DWORD PTR [ebp-4], eax ; x = 21
mov eax, DWORD PTR [ebp+12] ; eax = b (42)
add eax, eax ; eax = b*2 (84)
mov DWORD PTR [ebp-8], eax ; y = 84
; 返回值计算(通过EAX传递):
mov eax, DWORD PTR [ebp-4] ; eax = x
add eax, DWORD PTR [ebp-8] ; eax = x+y (105)
; --- 上下文恢复关键步骤 ---
mov esp, ebp ; 1. ESP回到当前帧基址(清除局部变量)
pop ebp ; 2. 恢复调用者EBP(从栈中弹出旧EBP)
ret ; 3. 弹出返回地址到EIP(自动ESP+=4)
栈帧结构与内存布局
- 当func函数执行时,堆栈呈现清晰的层次结构(高地址→低地址)
cpp
高地址(内存高位)
+---------------------+ ← 初始 ESP (main 调用前)
| main 的局部变量 | (16字节,main 中 sub esp,16 分配)
| ... (result 等) |
+---------------------+ ← main 的 EBP (0x0FFC)
| 保存的旧 EBP (main) | ← [main_ebp] = 0x2000 (main 的调用者帧基址)
+---------------------+
| 参数 b = 42 | ← [ebp + 12] (main 中 push 42)
+---------------------+
| 参数 a = 20 | ← [ebp + 8] (main 中 push 20)
+---------------------+
| 返回地址 (EIP) | ← [ebp + 4] (call func 压入)
+---------------------+ ← func 的 EBP (0x0FD0) ← EBP 寄存器当前值
| 保存的旧 EBP (main) | ← [ebp] (func 中 push ebp)
+---------------------+
| 局部变量 y = 84 | ← [ebp - 4] (func 中分配)
+---------------------+
| 局部变量 x = 21 | ← [ebp - 8] (func 中分配)
+---------------------+ ← func 的 ESP (0x0FC8) ← ESP 寄存器当前值
| |
| 空闲栈空间 |
| |
低地址(内存低位)
参数 局部变量 esp ebp的关联说明
-
EBP 是栈帧基址指针 :在函数内部 固定不变 ,作为访问参数/局部变量的 唯一基准
-
ESP 是栈顶指针 : 动态变化 ,随压栈/出栈操作实时移动
-
参数在 EBP 上方 (正偏移):因为调用者先压参数,再压返回地址
-
局部变量在 EBP 下方 (负偏移):被调用者通过
sub esp, N分配
func 函数执行期间
EBP 的核心作用:栈帧基址锚点
-
EBP 寄存器值 =
0x0FD0(指向当前函数的栈帧基址) -
EBP 指向的位置 :保存了调用者(main)的 EBP 值 (
0x0FFC) -
为什么固定?
mov ebp, esp执行后,EBP 不再随局部变量分配而改变,始终指向:
cpp
[ebp] = 保存的旧 EBP (main 的帧基址)
参数访问:EBP + 正偏移(在 EBP 上方)
cpp
偏移量 计算方式 物理位置 值 说明
4 [ebp + 4] 0x0FD4 EIP 返回地址(call 指令压入)
8 [ebp + 8] 0x0FD8 20 第1个参数 a(main 后压)
12 [ebp + 12] 0x0FDC 42 第2个参数 b(main 先压)
- 第 N 个参数 = [EBP + 4(N+1)] * (因为
[ebp+4]是返回地址,参数从[ebp+8]开始)
局部变量访问:EBP - 负偏移(在 EBP 下方)
cpp
偏移量 计算方式 物理位置 值 说明
-4 [ebp - 4] 0x0FCC 21 局部变量 x(先分配)
-8 [ebp - 8] 0x0FC8 84 局部变量 y(后分配)
- 第 K 个局部变量 = [EBP - 4*K] (按声明顺序从高地址向低地址分配)
ESP 的动态变化
|--------------------|-----------|--------|--------------------|
| 指令 | ESP 变化 | ESP 值 | 说明 |
| push ebp (func 开始) | ESP -= 4 | 0x0FD0 | 压入旧 EBP(main 的帧基址) |
| mov ebp, esp | 无变化 | 0x0FD0 | EBP 锚定当前帧基址 |
| sub esp, 8 | ESP -= 8 | 0x0FC8 | 分配局部变量空间(x,y 各4字节) |
| mov esp, ebp (返回时) | ESP = EBP | 0x0FD0 | 丢弃局部变量(ESP 回到帧基址) |
| pop ebp | ESP += 4 | 0x0FD4 | 恢复调用者 EBP(加载到寄存器) |
| ret | ESP += 4 | 0x0FD8 | 弹出返回地址(跳转回 main) |