> 文章列表 > cpp 程序与 c 程序的相互调用(extern “C“ 的使用)

cpp 程序与 c 程序的相互调用(extern “C“ 的使用)

cpp 程序与 c 程序的相互调用(extern “C“ 的使用)

实际在编译的过程中,.cpp 文件调用 .c文件中的函数会出错。假设代码结构如下:


目录

一、编译过程分析

1、预处理

2、编译

3、汇编

4、链接

二、问题解决

1、解决方案

2、解决思路


一、编译过程分析

1、预处理

该阶段头文件会被展开,此时只剩下 Add.c 和 main.cpp 文件。

2、编译

该阶段除了做一些语法、词法、语义分析、转换成汇编代码外,还会进行符号汇总

3、汇编

该阶段的目的是将汇编代码转换成二进制机器码,除此之外,还会生成符号表。符号名的修饰规则依赖于使用的编译器.cpp 文件默认使用的是 c++编译器, 而 .c 文件默认使用的是 c 编译器

  • C 编译器的命名规则:直接将函数名作为符号名,不做任何修饰
  • C++编译器的命名规则:__Z + 返回值类型 + 函数名 + 形参类型

4、链接

在这个阶段,我们目前只关注一个功能,那就是将不同来自不同源文件的符号表汇总

汇总以后发现,main.cpp 中用到了 Add 函数,因为在 .cpp 文件中,那么经过C++编译器生成的符号名就是__ZiAddii ,结果发现找不到对应的函数地址,这个时候就会报错。

VS环境下的报错是:

Linux环境下的报错是: 

二、问题解决

下面先贴出解决方案,至于原因,感兴趣的可以继续往下看

1、解决方案

只要将函数声明放在下面指示的地方即可

#ifdef __cplusplus
extern "C" {
#endif// 你的函数声明#ifdef __cplusplus
}
#endif

2、解决思路

最初的思路

根本原因在于 .c 文件和 .cpp 文件都引入了 Add.h,然而生成符号表的时候,却是使用了不同的命名规则,这就导致了得到了不同的符号名。

我们要保证的是,无论是 C 还是 C++ 编译器,得到的符号名是一样的,因此就引入了 extern "C",extern "C"可以保证自身作用域里的内容,是按照 C 编译器的修饰规则去编译的,这样的话得到的符号名就都是 C编译器的编译出来的结果。

extern "C" {int Add(int x, int y);
}

偶遇小坑

预处理阶段 Add.c 引入头文件 Add.h 并展开的时候,就变成了如下的样子,我们发现报错了,这是因为 C 程序虽然可以识别 extern 关键字,但是无法识别 extern "C" 这种形式

填坑!完事!

.cpp 文件和 .c 文件都会引入 Add.h 文件,cpp文件默认使用的是 C++编译器,而 c 文件默认使用的是C编译器,那我们的 Add.h 就有必要自己识别当前自己是在 cpp 文件被展开,还是在 c 文件被展开

这就要用到宏 __cplusplus,C++中定义了该宏,C却没有定义,因此如果在 cpp 文件中被展开,就需要加上 extern "C"; 如果是在 c 文件中被展开,就无需加上。

#ifdef __cplusplus
extern "C" {
#endifint Add(int x, int y);#ifdef __cplusplus
}
#endif

手机网游