C/C++代码的混合使用
一、extern "C"
C/C++的代码要混合使用,肯定要用到extern "C"了。extern "C"暗示编译器,被它修饰的内容使用C的链接风格,什么意思呢,直接上代码:
1 //sample.cpp 2 3 void fun(int a) 4 { 5 } 6 7 void fun(int a, double b) 8 { 9 } 10 11 extern "C" void foo(int a) 12 { 13 }
这里有三个函数,首先是重载的两个fun函数,然后是用extern "C"修饰的foo函数。用g++编译一下,然后查看下导出的符号:
发现了吧,两个fun函数编译后导出的符号是前缀"_Z3"加上函数名,再依次加上参数类型的简写。所以void fun(int a)变成了_Z3funi,void fun(int a, double b)变成了_Z3funid。这样做是为了支持函数重载,即,只要参数不同就可以认为是不同的函数。而用extern "C"修饰的函数foo,编译后导出的符号仅仅只是函数名。这就是C的链接风格,当然这样函数重载也就没办法支持了。
用了extern "C",使得无论是C还是C++的代码,编译过后针对同一个函数使用相同的符号,这样链接到一起的时候就不会报“undefined reference"了。实际使用时有两种场合,要么是C++代码调用C代码,要么是C代码调用C++代码。
顺便提一下,extern "C"只能写在C++代码中,C编译器是不认的。
二、C++调用C
还是直接看示例代码清晰明了:
1 //caller.cpp 2 3 #include <iostream> 4 5 using namespace std; 6 7 extern "C" void fun(void); 8 9 int main() 10 { 11 cout << "I'm caller.." << endl; 12 fun(); 13 }
1 //fun.c 2 3 #include <stdio.h> 4 5 void fun(void) 6 { 7 printf("This is C function \"fun()\"\n"); 8 }
这样两个文件,简单编译运行一下:
当然,如果把extern "C" void fun(void);放到头文件中风格会更好。本人比较懒,这里的简单示例就不按规矩写了。
三、C调用C++
这部分写了两个例子,首先是调用C++的普通函数,然后是调用类成员函数。
同样看代码,不过,先看一下被调用者C++代码:
1 //fun.cpp 2 3 #include <iostream> 4 using namespace std; 5 6 void fun(int a) 7 { 8 cout << "This is cpp function \"fun(" << a << ")\"" << endl; 9 } 10 11 void fun(int a, double b) 12 { 13 cout << "This is cpp function \"fun(" << a << ", " << b << ")\"" << endl; 14 } 15 16 extern "C" void foo(int a) 17 { 18 cout << "This is cpp function \"foo(" << a << ")\"" << endl; 19 } 20 21 extern "C" { 22 23 void fun_i(int a) 24 { 25 fun(a); 26 } 27 28 void fun_d(int a, double b) 29 { 30 fun(a, b); 31 } 32 33 }
这里有5个函数,两个重载的fun函数,剩下三个函数foo, fun_i, fun_d全都用extern "C"修饰了,导出给C代码调用。关于extern "C"的两种写法,一目了然,我就不解释了。这里的fun_i和fun_d只是把fun的两个重载版本分别包装了一下,因为C是没有函数重载的概念的。foo没有重载,所以也没有包装函数。事实上,如果知道C++编译器命名符号的规则(本文一开始已讨论过),不用extern "C",不用包装函数,直接使用符号调用也是可以的。这种方式在下面caller.c中也有示范:
1 //caller.c 2 3 #include <stdio.h> 4 5 int main(int argc, const char *argv[]) 6 { 7 printf("I'm C caller...\n"); 8 9 fun_i(1); 10 fun_d(2, 2.1); 11 foo(3); 12 13 _Z3funi(4); 14 _Z3funid(5, 5.1); 15 16 return 0; 17 }
编译运行:
可见,用包装函数调用和用符号调用都可以工作。我这里链接了libstdc++库,因为我是用gcc编译的,如果用的是g++,libstdc++是会默认链接的。这就涉及到项目主体是C还是C++的问题了,如果主体是C代码,必须用gcc编译,但是又引用了C++写的库,这时候链接一定要加上libstdc++。反之,如果主体是C++,用g++编译不会有类似的问题。
下面再提供一个用C代码调用C++类成员函数的例子,用包装函数:
1 //fun.cpp 2 3 #include <iostream> 4 using namespace std; 5 6 class shit 7 { 8 public: 9 shit(){} 10 ~shit(){} 11 void happen(); 12 }; 13 14 void shit::happen() 15 { 16 cout << "Holy shit!!" << endl; 17 } 18 19 extern "C" 20 void run() 21 { 22 shit fresh_shit; 23 fresh_shit.happen(); 24 }
1 //caller.c 2 3 #include <stdio.h> 4 5 int main(int argc, const char *argv[]) 6 { 7 printf("I'm C caller...\n"); 8 run(); 9 10 return 0; 11 }
编译运行: