我们经常再写代码时会使用C与C++混合编程,但是在调用对方接口时我们为了避免出现问题会使用extern “C”。
1、出现的问题
在C++中,为了支持重载机制,在编译时,要对函数的名字进行一些处理,比如加入函数的返回类型等来加以区别;这也就是C++中的函数命名规则。如函数int sum(int a,int b)在编译过程中会生成 ?sum@@YAHHH@Z这样的符号,这也是C++可以实现重载的原因。
在 C中,只是简单的函数名字而已。C则把该函数编译成类似_sum的符号,C链接器只要找到该函数符号就可以链接成功,它假设参数类型信息是正确的。
所以,关键问题是,C和C++在编译时生成函数名字的方式是不同的。所以会出现调用不成功的现象。
2、方法
所以我们使用extern “C”。它是指在C++中告诉编译器按照C的规则来处理,直接使用函数名而不是重新生成。
3、C++调用C
//Compare.c
int Compare(int a,int b)
{
return a>b?1:-1;
}
//main.cpp
extern "C"
{
extern int Compare(int int);//这样就会生成_Compare的符号和c语言符号保持一致。
}
int main()
{
Compare(10,20);
return 0;
}
这样就可以调用成功。
也就是大致规则是这样
extern "C"
{
//代码块;
}
//extern "C"中包含的代码块以C语言规则处理
4、C调用C++
//Compare.c
int Compare(int a,int b)
// {
// return a > b ? 1 : -1;
//}
int main()
{
Compare(10,20);
return 0;
}
我们现在在.c文件中给出主函数,而且只给出函数的声明。
在主函数中给出函数的声明,也就是在c语言中调用C++的代码。
//main.cpp
int Compare(int a,int b)
{
return a>b ? 1 : -1;
}
这样我们发现是无法调用成功的。也就是只能是后者调用前者无法前者调用后者。因为是C出现比C++早。所以我们只需要在C++中改。加入extern "C"以及整个函数的实现。具体如下:
//main.cpp
extern "C"
{
int Compare(int a,int b)
{
return a>b ? 1 : -1;
}
}
这样就可以调用成功。但是这种情况也是基于C++的文件可以被改动的情况下。
那么不能改动的情况下又是怎样的?
5、C调用C++的库函数(C++的文件不能被改动)
这时候我们加中间层,这个中间层使我们要调用函数时自己写的函数。
int Mycompare(int a,int b)
{
return Compare(a,b);
}
然后只需要在.c中调用Mycompare函数即可。
//middle.cpp
int Mycompare(int a,int b);
int main()
{
Mycompare(10,20);
}
然后在我们自己写的函数中加extern “C”
//middle.cpp
int Compare(int a,int b);
extern "C"
{
int Mycompare(int a,int b)
{
return Compare(a,b);
}
}
这样就可以调用成功。
6、函数所在的文件不确定以怎样的编译器编译
我们使用C++特有的宏 __cplusplus来处理。
//sum.cpp
#ifdef __cplusplus
extern "C"
{
#endif
int Sum(int a,int b)
{
return a + b;
}
#ifdef __cplusplus
}
#end
a.c
#ifdef __cplusplus
extern "C"
{
#endif
int Sum(int a,int b)
{
return a + b;
}
#ifdef __cplusplus
}
#end
sum.c
int Sum(int,int)
int main()
{
sum(10,20);
rerurn 0;
}
我们在C和C++的文件中同时写成上述的代码,我们会发现在C语言中都能被调用成功。原因是:因为__cplusplus是C++特有的宏所以在C++中会命中第一行的extern "C"这样就会在C++中形成这样的代码:
extern "C"
{
int Sum(int a,int b)
{
return a + b;
}
}
这样在C++中有extern "C"会以C语言的规则来处理也就是会生成_sum的符号。这样就会调用成功。
但是在C中因为没有 __cplusplus这样的宏,它的第一行就不能命中也就是没有extern “C”,他就会形成这样的代码:
int Sum(int a,int b)
{
return a + b;
}
这样生成的符号也是_sum所以他也能调动成功。
所以当我们不确定是以哪种编译器编译时,写成这样的代码,加入__cplusplus宏,在C和C++中都可以调用成功。