我们经常在C++代码中看到下面的代码:
#ifdef __cplusplus
extern "C" {
int func();
}
#endif
我们知道__cplusplus是c++编译器预定义的宏,上面这段代码只要有在使用C++编译器进行代码编译的时候,才会起作用。
那么,extern "C" { }是做什么的呢?
在回到这个问题之前,需要了解“函数调用约定”:
常见的函数调用约定有四种:cdecl、stdcall、fastcall、thiscall。他们在函数参数压栈顺序、堆栈恢复和函数名等方面有差别:
调用方式 | 参数压栈方式 | 恢复堆栈 | 函数名 | 备注 |
stdcall | 参数从右向左压入堆栈 | 函数自身修改堆栈 | 函数名自动加前导的下划线,后面紧跟一个@符号,其后紧跟着参数的尺寸 | 被称为pascal调用 |
cdecl | 参数从右向左压入堆栈 | 调用者负责清理堆栈 | 自动在函数名前加前导的下划线 | C语言缺省的调用约定,支持可变参数 |
fastcall | 第一个和第二个DWORD参数(或者尺寸更小的)通过ecx和edx传递,其他参数通过从右向左的顺序压栈 | 函数自身修改堆栈 | 函数名自动加前导的下划线,后面紧跟一个@符号,其后紧跟着参数的尺寸 | 除了前面两个参数压栈方式,其他和stdcall相同 |
thiscall | 参数从右向左压入堆栈,如果参数个数确定,this指针通过ecx传递给被调用者;如果参数个数不确定,this指针在所有参数压栈后被压入堆栈 | 参数个数不定的,调用者清理堆栈,否则函数自己清理堆栈 | 仅适用于C++ | C++类成员函数缺省的调用约定 |
注意:不同的调用约定,编译器会对函数名进行修改,如函数int func(int a,int b),使用cdecl调用是,函数名被修改为_func,而stdcall调用时,函数名被修改为_func@8。
extern "C" { }表示{}中的代码使用C语言的方式进行编译和链接。这样,全部都是用cdecl调用约定,在C和C++混编的时候,就不会出现链接时找不到函数定义的问题。