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 }

编译运行:

posted @ 2013-10-25 23:25  suosuopuo  阅读(332)  评论(0编辑  收藏  举报