extern “C”
extern "C"的作用
被extern “C“修饰的代码会按照C语言的方式去编译。声明需要使用extern C,实现不需要。
如果extern C加在实现上,会出现链接方式的冲突问题。
extern "C"{
void dosomething();
int getIndex();
const char* getName();
}
void dosomething()
{
}
int getIndex()
{
}
const char* getName()
{
}
首先,C++是支持函数重载的, c语言不支持
//function.cpp---编译通过
void func()
{...}
void func(int var)
{...}
//function.c----编译不通过
void func()
{...}
void func(int var)
{...}
例如上面两个函数,编译器知晓这是C++源文件,支持函数的重载。所以能够编译的过去,那么如果我用extern "C"去修饰,会有什么结果?
//function.cpp---编译通过
extern "C" void func()
{...}
extern "C" void func(int var)
{...}
//或者
extern "C"{
void func(){...}
void func(int var){...}
}
使用vs2017的编译环境。。编译后,ide给出的错误提示是:
- error C2733: 不允许重载函数“func”的第二个C链接。
当使用extern C修饰的时候,函数会被编译器按照C语言的方式去编译。但是由于C语言是不支持函数重载的,这就意味着第二个函数是不会许可的。
按照编译器生成的的函数标签
//如果是C语言函数
void func()->可能的函数标签就是func
//如果是C++函数,由于C++支持函数重载
void func()->func_void_emtpy_var
void func(int var)->func_void_int_var
可以看到,编译器对C语言的函数和C++的函数生成的函数标签不一样。
什么时候用到extern C
我们在项目开发中,遇到第三方的库,第三方库可能是用C语言编写的,给我们的接口就是C语言函数接口,比如mysql的库等
举例:
//mymath.c
int sum(int a, int b)
{
return a + b;
}
//main.cpp
#include <iostream>
using namespace std;
//使用extern "C"修饰
extern "C"
{
int sum(int a, int b);
}
int main()
{
int res = sum(3, 2);
std::cout << "res = " << res << std::endl;
return 0;
}
上面例子中,使用extern C修饰了sum函数。如果不用,C++会认为这是一个C++函数,函数签名很大可能会变成sum_int_int,而在mymath.c中只有sum这个签名,所以会导致一个大家很常见的连接错误:"无法解析的外部符号
int _cdecl sum(int, int);该函数在main.cpp中被引用"。使用extern C修饰后,签名一致,能够找到函数了,链接也通过。
当我们写C语言函数接口给别人使用
//一个很简单的例子
//mymath.h
//第二种办法,使用CPLUSPLUS宏定义处理extern C
#ifdef __cplusplus
extern "C"
{
#endif
int sum(int a, int b);
int del(int a, int b);
#ifdef __cplusplus
}
#endif
//mymath.c
int sum(int a, int b)
{
...
}
int del(int a, int b)
{
...
}
#include <iostream>
//第一种方法,不处理mymath.h的头文件,直接extern “c”包含进来
extern "C"{
#include "mymath.h"
}
//第二种办法--符合常理
#include "mymath.h"
int main()
{
int ret = sum(3, 2);
return 0;
}
解释一下第二种办法:如果按照第一种办法,好处不需要对mymath.h的头文件做任何处理。cpp文件,c文件都可以调用mymath.h的功能。但是一般来说,这个处理的比较少。所以第二种办法使用的比较多。对于调用者,我们直接#include “header file”即可,库提供者或者源码提供者自己做处理。
当cpp调用mymath头文件,我们使用extern “C”修饰。当C语言调用该头文件,由于extern “C”属于c++编译环境下的产物,会导致编译不过去,不能识别extern C。那么由于对于cpp文件,编译器会给该文件自动添加一个宏__cplusplus来标明是cpp,所以我们利用这个宏,可以做到一个开关作用,用这个宏来修饰extern “C”。让它对不同的编译环境起到不同的作用,使得C和C++调用均可。