C 与 C++ 之间的相互调用及函数区别
最近项目需要使用 google test(以下简称为 gtest)作为单元测试框架,但是项目本身过于庞大,main 函数无从找起,需要将 gtest 框架编译成静态库使用。因为项目本身是通过纯 c 语言编写,而 gtest 则是一个 c++编写的测试框架,其中必然涉及 c 与 c++之间的相互调用。
注意,本文的前提是,c 代码采用 gcc 等 c 语言编译器编译 c 代码,采用 g++等 c++编译器编译 c++代码,如果 c 和 c++代码统一使用 g++编译,大部分情况是可以实现两者代码相互调用的。以下为踩坑过程的总结 o_O||。
c 与 c++的函数区别
要了解两者之间如何实现相互调用,必须先了解 c 与 c++之间的函数有什么不同。
c++作为 c 语言的升级版,两者必然有很多不同之处。其中有一个重大不同点就是,c++支持函数重载,而 c 语言不支持。为了使函数支持重载,c++在 c 语言的基础上,将函数名添加上返回值和参数的类型信息。
例如,int add(int, int)
这个函数,通过 c++编译器编译后,可能呈现的函数名为int int_add_int_int(int, int)
(注:此处为大概地说明 c++是如何将返回值和参数信息添加到函数名中的,实际中编译器不一定是这样实现的)。
从以上说明可以得出,由于 c++对函数重载的支持,使得编译后的函数符号与 c 语言的不一致,即使是在两者函数名相同的前提下。
C++项目:
C 项目:需要头文件,头文件是提供给主程序包含的。
因为是 C++调用 C,而一个程序只有一个 main 函数,所以 C 项目里是没有 main 函数的。
extern "C"的作用
那么,c 与 c++是不能相互调用了吗?答案是否定的,因为存在着extern "C"
这个关键字可以使语句可以按照类 C 的编译和连接规约来编译和连接,而不是 C++的编译的连接规约。这样在类 C 的代码中就可以调用 C++的函数 or 变量等。
注意:extern "C"
指令中的"C",表示的一种编译和连接规约,而不是一种语言。"C"表示符合 C 语言的编译和连接规约的任何语言,如 Fortran、assembler 等。
还有要说明的是,extern "C"
指令仅指定编译和连接规约,但不影响语义。例如在函数声明中,指定了extern "C"
,仍然要遵守 C++的类型检测、参数转换规则。
c++中调用 c 代码
对于 c++,由于 c++的编译器对 c 语言兼容,因此在 c++中调用 c 语言编写的函数,只需要在函数声明前面加上关键字extern "C"
,表示采用类 c 语言的方式解析函数符号。例子如下:
在例子中,main.cc 为 c++代码,add.c 为 c 语言代码,当 c++编译器识别到extern "C"
`关键字时,会去寻找 add 函数的实现而不是寻找类似 int_add_int_int 这样带参数信息的函数实现。
c 语言调用 c++代码
c 语言调用 c++代码却并不容易,原因是 c 语言并不兼容 c++。就算 c 语言可以调用 c++,也会因为无法识别 c++新定义的符号而编译报错。因此,为了实现 c 语言调用 c++函数,必须实现以下两个步骤:1. 将 c++相关函数封装为静态库或动态库(因为调用库函数时编译器并不知道里面执行的是什么语言);2. 对外提供遵循类 c 语言规约的接口函数。例子如下所示:
通过将 cout 函数封装为类 c 语言规约的接口函数,使得 main.c 中可以成功调用 c++函数 printNum。值得注意的是,main.c 不可以直接引入 printNum.h,因为 c 语言不能识别extern "C"
关键字。可以利用 c++预定义宏实现头文件的改写:
小结
c 语言与 c++的相互调用可以通过
extern "C"
关键字实现c++中调用 c 代码,只须在 c++中为 c 代码函数声明之前加上
extern "C"
c 语言调用 c++代码,则需要将 c++代码编译成静态库或动态库,然后对外提供用
extern "C"
声明的类 c 封装函数
版权声明: 本文为 InfoQ 作者【向阳逐梦】的原创文章。
原文链接:【http://xie.infoq.cn/article/481ac4ceb9bd7a1c3bd5eb1e8】。文章转载请联系作者。
评论