C++做久了,经常用C++的方式去思考问题,有时候就突然发现自己不太会写C程序了。写程序的时候,难免会用到第三方插件或者是库,而这些插件或者库很多时候都不能完全满足我们的需求,遇到这种情况,如果全是C++,那好办,写个适配器就OK了,关于适配器模式参考我的博客《C++ Adaptor 设计模式》 如果要提供给C程序使用,那就需要自己封装C程序可以使用的库。前几天在CSDN Linux/Unix版闲逛的时候,遇到一位网友,他问这样的问题:(原话我记不住了,大致是这个意思)我要要封装一个C++接口提供给C程序使用,C++程序完全没有问题,可以运行,但是封装的时候,我使用g++编译,就没问题,但是预期的结果是使用gcc编译程序,可是使用gcc编译的时候确保错,很多 undefined...我想熟悉Linux编程的人一看这个问题都知道是怎么回事。这里我就不标新立异了,我只想总结下如果让C调用C++接口
再将接口封装之前先将建C/C++的一些特性有助于后面的理解。
C++创始人在编写C++的时候,C语言正盛行,他不得不让C++兼容C。C++最大的特性就是封装,继承,多态,重载。而这些特性恰恰是C语言所不具备的。至于多态,核心技术是通过虚函数表实现的,其实也就是指针。而对于重载,与C语言相比,其实就是编译方式不同而已: C++编译方式和C编译方式。对于函数调用,编译器只要知道函数的参数类型和返回值以及函数名就可以进行编译连接。那么为了让C调用C++接口或者是说C++调用C接口,就必须是调用者和被调用者有着同样的编译方式。这既是extern "C"的作用,extern “C”是的程序按照C的方式编译。我们先来看看C++和C两种编译方式对于究竟有何不同,由于C只考虑函数调用,这里只讨论函数的差别。下来看一段源代码:
我们用一个很简短的代码说明问题:
//cplus.cpp //按照C++方式编译程序 int Operation(int)
键入命令编译cplus: g++ -c cplus.cpp -o cplus.o 产生了目标文件cplus.o。我们来看看该目标文件中的符号
使用nm命令查看内部符号:nm cplus.o
内容很简单:00000000 T _Z9Operationi
再来看看加 exern "C"按照C方式编译程序:
extern "C" int Operation(int)
同样使用上面的命令产生cplus.o。然后查看符号如下:
00000000 T Operation
对比下可以发现,使用C++方式编译函数多了个_Z9前缀和i后缀,其中i指的是参数类型。这下明白了,因为C不存在重载,只需要知道函数名称就可以确定函数,而C++有重载,需要根据参数类型和返回类型才可以唯一确定一个函数。
说道这里,大家估计已经理解的差不多了。提供给C的接口必须加 extern "C"。这里还只是确定了编译方式,extern "C"只能让编译器安C的方式编译。但是C并不认识
extern "C",这里还要加一道工序:在C文件中 extern下接口。这样C程序就认识接口函数了。下面以一个简单的例子来说明具体如何让封装C++接口给C使用。
//myclass.h #include <iostream> using namespace std; class Myclass { public: Myclass(){} ~Myclass(){} void Operation(); }; //myclass.cpp extern "C" void Myclass::Operation { cout << "Hi, Harlen" << endl; }
编译命令:g++ -c myclass.cpp -o myclass.o
//interface.cpp #include "myclass.h" void interface() { Myclass obj; obj.Operation(); }
编译命令:g++ -c interface.cpp -o interface.o
这样,其实接口就已经准备好了。一种方式是使用命令:ar rs libinterface.a interface.o myclass.o产生静态库提供接口。
另一种方式是使用gcc,将调用程序的.o目标文件和myclass.o, interface.o一起编译成可执行程序。
//main.c extern interface(); int main(int argc, char**argv) { interface() }
编译:gcc -c main.c -o main.o
:gcc -o main interface.o myclass.o -lstdc++到此为止,接口已经提供完成。C程序中就可以使用interface接口了。