>

这篇文章主要介绍下 C 和 C++ 程序之间互相调用需要知道基本知识,作为一个 C++ 程序员,虽然不爱写 C 风格的代码,但是由于项目中难免需要使用到 C 语言的库,了解这部分原理也是有助于加深对 C++ 语言机制的理解的。

如何调用

C++ 和 C 的编译链接处理方式完全不同,编译器生成函数名所用的规则不同。如果直接在 C++ 里面直接调用 C 函数,会找不到函数体,报链接错误。本文介绍如何在 C++ 中调用 C 函数。

简单来说,只需要在 C++ 程序中把要调用的 C 函数添加一个 extern "C" 声明。如下,

1
2
3
4
5
6
7
extern “C” void foo(int x, int y);

// or
extern “C”{
int foo(int x, int y);
void bar();
}

如果需要调用 header 文件中定义的全部函数,可以把相应的 C 头文件添加 extern "C" 声明,如下:

1
2
3
extern "C" {
#include “myheader.h”
}

内在原理

C++ 语言支持函数重载,而 C 语言不支持函数重载。函数经过 C++ 编译器编译之后所生成的名字与 C 编译器所生成的不同。
添加 extern "C" 的目的就是告诉 C++ 编译器哪些函数是 C 写的,要用 C 的方式来处理。

假设某个 C 函数的声明如下:

1
void foo(int x, int y);

该函数被 C 编译器编译后在库中的名字为 _foo,而 C++ 编译器则会产生像 _foo_int_int 之类的名字用(为了支持函数重载和类型安全连接)。由于编译时生成函数名字的规则不同,C++ 程序在链接时按照其名字规则去寻找函数实现时就会找不到对应的函数。
因此,C++ 中不能直接调用 C 函数。C++ 提供了一个 C 连接交换指定符号 extern “C” 来解决这个问题。

在 C++ 程序中给要调用的 C 函数添加 Extern “C” 就是告诉 C++ 编译译器,函数 foo 是个 C 连接,链接目标文件时应该按照 C 函数的函数名规则到库中去找名字 _foo ,而不是找 _foo_int_int

C++ 编译器开发商已经对 C 标准库的头文件作了 extern “C” 处理,所以可以用 #include 直接引用这些头文件之后就可以直接调用了。

在 C++ 程序通添加 extern “C” 这种方式有一定的缺陷,比如会导致 C++ 代码比较繁冗复杂,无法保持代码的整洁,同时还需要程序员维护调用来源上下文,对 C++ 程序有一定的侵入。

通常,C++ 中需要调用 C 函数的场景是,已经有一个编译好的 C 动态库(如 .so 文件),需要直接使用它提供的功能而避免重复开发。

如果可以修改 C 源码的头文件,那么可以用如下方式来处理,

在 C 的头文件的开头加上:

1
2
3
#ifdef __cplusplus
extern "C" {
#endif

同时,在头文件的末尾加上:

1
2
3
#ifdef __cplusplus
}
#endif

这样的作用就是把头文件中所声明的所有函数都声明为在 C++ 调用时使用 C 的方式去链接函数,就不用在 C++ 代码中添加 extern “C” 了,
C++ 程序中可以直接用 #include<*.h> 来引入用户自己定义的 C 函数所在的头文件,从而,C++ 的代码就简洁了很多!


Reference
How to mix C and C++