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" {
#endif

	int Add(int x, int y);

#ifdef __cplusplus
}
#endif
  • 9
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值