目录
[二、函数调用过程(以 x86 cdecl 为例)](#二、函数调用过程(以 x86 cdecl 为例))
[三、x86 vs x64 区别](#三、x86 vs x64 区别)
[四、示例分析(C代码 → 汇编)](#四、示例分析(C代码 → 汇编))
一、核心概念
-
调用约定 (Calling Convention)
-
规定参数传递顺序(如
cdecl
是右到左)、栈清理责任(调用者或被调用者清理)。 -
常见约定:
cdecl
(C默认)、stdcall
(Win32 API)、fastcall
(寄存器传参)。
-
-
栈帧 (Stack Frame)
-
每个函数调用时在栈上分配的内存块,保存返回地址、参数、局部变量等。
-
通过
EBP
(基址指针)和ESP
(栈指针)管理。
-
-
关键寄存器
-
EIP: 指向下一条要执行的指令(不可直接修改)。
-
ESP: 栈顶指针。
-
EBP: 基址指针,标记当前栈帧的起点。
-
二、函数调用过程(以 x86 cdecl
为例)
- 调用者 (Caller) 准备参数
cpp
push 3 ; 第三个参数(假设从右向左压栈)
push 2 ; 第二个参数
push 1 ; 第一个参数
call my_func ; 1. 将返回地址压栈 2. 跳转到 my_func
add esp, 12 ; 调用者清理栈(cdecl 约定)
- 被调用函数 (Callee) 建立栈帧
cpp
my_func:
push ebp ; 保存旧基址指针
mov ebp, esp ; 新基址指针指向当前栈顶
sub esp, 8 ; 分配8字节局部变量空间
此时栈结构:
cpp
[ebp+12] 参数3
[ebp+8] 参数2
[ebp+4] 参数1
[ebp] 旧EBP
[ebp-4] 局部变量1
[ebp-8] 局部变量2
- 函数执行
通过 EBP
偏移访问参数和局部变量:
cpp
mov eax, [ebp+8] ; 取第一个参数
mov [ebp-4], eax ; 存入局部变量1
- 函数返回
cpp
mov eax, 42 ; 返回值存EAX
mov esp, ebp ; 恢复ESP到栈帧起点
pop ebp ; 恢复旧EBP
ret ; 弹出返回地址,跳回调用者
三、x86 vs x64 区别

四、示例分析(C代码 → 汇编)
假设有函数:
cpp
int add(int a, int b) {
return a + b;
}
对应 x86 汇编:
cpp
add:
push ebp
mov ebp, esp
mov eax, [ebp+8] ; 取a
add eax, [ebp+12] ; 加b
mov esp, ebp
pop ebp
ret
调用代码:
cpp
push 2
push 3
call add
add esp, 8 ; 清理栈
; 结果在EAX中
五、常见问题
-
栈溢出:递归过深或局部变量过大导致栈破坏。
-
调用约定不匹配 :如误用
stdcall
未清理栈,引发崩溃。 -
寄存器保存 :被调用函数需保存
EBX
,ESI
,EDI
(按约定)。