详解C/C++函数调用约定:从__cdecl到名字修饰
C/C++编程中,函数调用约定决定了参数如何传递给函数以及由谁负责清理栈内存。了解__cdecl、__stdcall等约定的差异,以及C和C++编译器生成的不同名字修饰(Name Mangling)规则,是解决链接错误、实现DLL导出和进行底层调试的关键。
C/C++函数调用约定概览
在32位Windows平台下,C和C++提供了多种函数调用约定,它们定义了寄存器使用和堆栈操作的行为。
1. __cdecl (C Default Calling Convention)
__cdecl是C/C++语言的默认调用约定。其核心特征包括:
- 参数入栈顺序:从右向左依次入栈。
- 栈清理责任:由调用者负责清理堆栈。这使得它天然支持可变参数函数(如
printf)。 - 返回值:通常存放在
EAX寄存器中。 - 符号修饰:函数名前加下划线前缀。见下方名字修饰说明。
int __cdecl add(int a, int b) {
int c = a + b;
return c;
}
int main(void) {
int x = 1, y = 2;
int z = add(x, y);
return 0;
}2. __stdcall
__stdcall常见于Windows API函数。与__cdecl的区别在于:
- 参数入栈:同样从右向左入栈。
- 栈清理责任:由被调用函数自身在返回前清理堆栈(使用
ret N指令,N为参数字节数)。 - 返回值:存放于
EAX寄存器。
3. __fastcall
旨在提高调用速度,通过CPU寄存器传递部分参数:
- 参数传递:前两个双字(DWORD)或更小的参数分别通过
ECX和EDX寄存器传递。 - 剩余参数:按照从右向左的顺序入栈。
- 栈清理:由被调用函数自身负责清理。
4. naked (裸函数)
naked不是一种独立的协议,必须结合其他约定使用(如_declspec(naked))。
- 编译器不为该函数生成进入和退出代码(如保存/恢复寄存器、调整栈帧)。
- 禁止使用return返回值,必须由程序员手动编写汇编来清理栈并返回结果。
5. __pascal
源自Pascal语言的古老调用约定。行为类似于__stdcall(参数逆序压栈,函数清理栈)。现代Visual Studio编译器已废弃此约定,开发中建议直接使用__stdcall。
6. __thiscall (C++特有)
专门用于C++类成员函数的调用约定:
- this指针:始终放置在
ECX寄存器中。 - 参数入栈:其余参数按从右向左顺序入栈。
- 栈清理:如果参数个数确定,由函数自身清理;如果不确定(如虚函数),通常由调用者处理。
class Object {
private:
int value;
int nums;
public:
Object(int x = 0, int y = 0) : value(x), nums(y) {}
void Print() const {
std::cout << value << std::endl;
}
};
int main(void) {
Object obj(10, 20);
obj.Print();
return 0;
}名字修饰约定
为了实现函数重载、区分不同的调用约定,编译器会对函数名进行“装饰”(Mangling),生成内部使用的唯一标识符。
1. C语言编译时的名字修饰规则
C语言不支持函数重载,修饰相对简单:
__cdecl:函数名前加一个下划线。例如add变为_add。__stdcall:函数名前加下划线,后跟@符号和参数字节总数。例如fun(a, b)(两整数8字节)变为_fun@8。__fastcall:函数名前加@符号,后接@符号及参数字节总数。例如fun(a)变为@fun@4。
// extern "C" 关闭名字修饰,保持原样输出供外部调用
extern "C" {
int __stdcall fun1(int a, int b) { return 0; }
int __cdecl fun2(int a) { return 0; }
int __fastcall fun3(int a) { return 0; }
}2. C++编译时的名字修饰规则
C++采用复杂的修饰算法以支持重载和类型安全,通常以?开头::
| 代号 | 含义 |
|---|---|
| X | void |
| H | int |
| J | long |
| PA | 指针 |
| Z | 无参或结束标记 |
extern "C++" {
// 以下仅为示意实际修饰名极长且复杂
void __stdcall fun_int(int a);
// 类似 ?fun_int@@YGXH@Z
}免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









