69 lines
2.8 KiB
Markdown
69 lines
2.8 KiB
Markdown
### 函数的调用约定有哪些,区别是什么
|
||
|
||
**1. `cdecl` (C Declaration)**
|
||
|
||
这是 C 语言的默认调用约定,也是最常见的
|
||
|
||
- **参数传递**:从**右到左**依次推入栈中
|
||
- **栈清理**:由**调用方(caller)**负责在函数返回后清理栈
|
||
- **寄存器**:`eax`, `ecx`, `edx` 是调用方保存的(caller-saved)寄存器。这意味着被调用函数可以自由使用这些寄存器,如果调用方需要保存它们,需要在调用前自己推入栈中
|
||
|
||
**优点**:支持可变参数函数,例如 `printf`
|
||
|
||
**缺点**:每次函数调用后都需要调用方执行额外的栈清理指令,会增加代码大小
|
||
|
||
**2. `stdcall` (Standard Call)**
|
||
|
||
这是 Windows API 的默认调用约定
|
||
|
||
- **参数传递**:从**右到左**依次推入栈中
|
||
- **栈清理**:由**被调用方(callee)**负责清理栈
|
||
- **寄存器**:与 `cdecl` 类似
|
||
|
||
**优点**:代码更精简。由于被调用方知道参数数量,只需一条指令即可清理栈
|
||
|
||
**缺点**:不支持可变参数函数,因为被调用方需要知道参数数量才能正确清理栈
|
||
|
||
**3. `fastcall`**
|
||
|
||
这是一个为了提高性能而设计的调用约定
|
||
|
||
- **参数传递**:前两个(或更多,具体取决于编译器和架构)参数通过**寄存器**传递,而不是通过栈。其余参数从右到左推入栈中
|
||
- **栈清理**:由**被调用方**清理栈
|
||
- **寄存器**:使用 `ecx` 和 `edx`(在 32 位 Windows 上)或 `rcx` 和 `rdx`(在 64 位 Windows 上)来传递前两个参数
|
||
|
||
**优点**:通过减少内存访问(栈操作)来提高函数调用速度
|
||
|
||
**缺点**:不支持可变参数函数
|
||
|
||
**4. `thiscall`**
|
||
|
||
这是 C++ 中非静态成员函数的默认调用约定
|
||
|
||
- **参数传递**:与 `cdecl` 或 `stdcall` 类似,但隐藏的 `this` 指针(指向对象实例)通过**寄存器**传递
|
||
- **栈清理**:由**被调用方**清理栈
|
||
|
||
**优点**:优化了 C++ 成员函数的调用
|
||
|
||
**缺点**:只能用于 C++ 成员函数
|
||
|
||
**5. `pascal` (Pascal Language)**
|
||
|
||
在老旧的 Windows 16 位编程中很常见,现在很少使用
|
||
|
||
- **参数传递**:从**左到右**推入栈中
|
||
- **栈清理**:由**被调用方**清理栈
|
||
|
||
**不同平台下的调用约定**
|
||
|
||
- **Windows (x86)**:
|
||
- `cdecl`:C/C++ 默认
|
||
- `stdcall`:Windows API 默认
|
||
- `fastcall`:用于性能优化
|
||
- `thiscall`:C++ 成员函数
|
||
- **Windows (x64)**:
|
||
- `__fastcall`:这是 64 位 Windows 的唯一调用约定。前四个整数或指针参数通过 `rcx`, `rdx`, `r8`, `r9` 寄存器传递。其余参数从右到左推入栈中
|
||
- **Linux (x86)**:
|
||
- `cdecl`:默认
|
||
- **Linux (x64)**:
|
||
- 前六个整数或指针参数通过 `rdi`, `rsi`, `rdx`, `rcx`, `r8`, `r9` 寄存器传递。其余参数从右到左推入栈中 |