写点什么

C 与 C++ 之间的相互调用及函数区别

作者:向阳逐梦
  • 2023-08-06
    四川
  • 本文字数:1700 字

    阅读完需:约 6 分钟

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 语言的方式解析函数符号。例子如下:

// add.h#ifdef __ADD_H__#define __ADD_H__extern "C" int add(int a, int b);#endif// add.cint add(int a, int b){    return a + b;}// main.cc#include <iostream>#include "add.h"using namespace std;int main(){    cout << "1 + 1 = " << add(1, 1) << endl;}
复制代码

在例子中,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 语言规约的接口函数。例子如下所示:

// printNum.h#ifdef __PRINTNUM_H__#define __PRINTNUM_H__extern "C" void printNum(int a);#endif// printNum.cc#include <iostream>#include "printNum.h"using namespace std;void printNum(int a){    cout << << "num is " << a << endl;}// main.cextern void printNum(int a);printNum(5);
复制代码

通过将 cout 函数封装为类 c 语言规约的接口函数,使得 main.c 中可以成功调用 c++函数 printNum。值得注意的是,main.c 不可以直接引入 printNum.h,因为 c 语言不能识别extern "C"关键字。可以利用 c++预定义宏实现头文件的改写:

#ifdef __PRINTNUM_H__#define __PRINTNUM_H__#ifdef __cplusplusextern "C" { #endifvoid printNum(int a);#ifdef __cplusplus}#endif#endif
复制代码

小结

  • c 语言与 c++的相互调用可以通过extern "C"关键字实现

  • c++中调用 c 代码,只须在 c++中为 c 代码函数声明之前加上extern "C"

  • c 语言调用 c++代码,则需要将 c++代码编译成静态库或动态库,然后对外提供用extern "C"声明的类 c 封装函数


发布于: 刚刚阅读数: 4
用户头像

向阳逐梦

关注

人生享受编程,编程造就人生! 2022-06-01 加入

某公司芯片测试工程师,嵌入式开发工程师,InfoQ签约作者,阿里云星级博主,华为云·云享专家。座右铭:向着太阳,追逐梦想!

评论

发布
暂无评论
C与C++之间的相互调用及函数区别_向阳逐梦_InfoQ写作社区