查看: 97|回复: 1

C与C++如何互相调用

[复制链接]

5

主题

11

帖子

18

积分

新手上路

Rank: 1

积分
18
发表于 2023-1-13 21:53:14 | 显示全部楼层 |阅读模式

在项目开发过程中,我们底层代码经常用C来实现,而上层应用大都会用C++实现,这样我们就涉及到了C和C++相互调用的情况了。那么,C/C++如何实现相互调用呢?
1、为什么会有差异?


  • 编译方式不同:C文件常采用gcc编译,而Cpp文件常采用g++来编译
  • C++支持函数重载:由于这一特性,C++和C中的同一个函数,经过编译后,生成的函数名称是不同的。
这样就导致了C与C++之间不能直接进行调用,要解决这一问题,就得靠extern "C"来辅助了。
2、extern "C"


  • extern
extern关键字我们并不陌生,它是编程语言中的一种属性,用来表示变量,函数等类型的作用范围。
我们经常在.c源文件中定义变量或者实现函数,在.h头文件中使用extern关键字进行声明,方便其他文件调用。


  • "C"
编程语言种类繁多,不同语言有不同的编译规则,如果想要互相调用,必须告诉编译器以什么规则去编译文件,这样才能正常调用。
其主要作用是:把“C”当作一个标志位,告诉编译器,下面代码以C的方式编译!
了解其中原理后,我们来实操一下!
3、C++调用C

我们创建3个文件,分别为main.cpp、cal.c、cal.h。



image-20221219154545328

我们分别使用gcc和g++单独编译文件,编译出cal.o和main.o两个中间文件,很简单,定义了一个embedded_art的函数。
# dong @ ubuntu in ~/WorkSpace/Donge_Programs/Unix_Programming_Learning/c_c++_call_test on git:main x [15:57:32]
$ ls
cal.c  cal.h  main.cpp

# dong @ ubuntu in ~/WorkSpace/Donge_Programs/Unix_Programming_Learning/c_c++_call_test on git:main x [15:57:43]
$ gcc -c cal.c

# dong @ ubuntu in ~/WorkSpace/Donge_Programs/Unix_Programming_Learning/c_c++_call_test on git:main x [15:57:49]
$ g++ -c main.cpp

# dong @ ubuntu in ~/WorkSpace/Donge_Programs/Unix_Programming_Learning/c_c++_call_test on git:main x [15:57:55]
$ ls
cal.c  cal.h  cal.o  main.cpp  main.o下面看一下编译之后的中间文件cal.o和main.o的符号表,看看同一个函数embedded_art不同编译方式之后的差别。



image-20221219160137798

可以看到,g++编译之后,对函数名称进行了加工,按照自身的编译规则,最终生成了一个新的函数名,所以我们如果直接调用cal.c中的embedded_art肯定是不行的。
正确方式

使用extern "C"来使g++编译器用C的方式编译。
在main.cpp文件中,我们引入cal.h的位置,添加extern "C"
extern "C" {
#include "cal.h"
}再次进行编译,即可!



image-20221219161410964

可以看到符号表中,该函数名称正常,然后我们将中间文件链接起来,执行,输出正确结果!
# dong @ ubuntu in ~/WorkSpace/Donge_Programs/Unix_Programming_Learning/c_c++_call_test on git:main x [16:18:36]
$ g++ main.o cal.o

# dong @ ubuntu in ~/WorkSpace/Donge_Programs/Unix_Programming_Learning/c_c++_call_test on git:main x [16:19:54]
$ ls
a.out  cal.c  cal.h  cal.o  main.cpp  main.o

# dong @ ubuntu in ~/WorkSpace/Donge_Programs/Unix_Programming_Learning/c_c++_call_test on git:main x [16:19:57]
$ ./a.out
main entry
嵌入式艺术4、C调用C++

我们创建3个文件,分别为main.c、cal.cpp、cal.h。



image-20221219162526678

我们分别使用gcc和g++单独编译文件,编译出cal.o和main.o两个中间文件,很简单,同样定义了一个embedded_art的函数。
# dong @ ubuntu in ~/WorkSpace/Donge_Programs/Unix_Programming_Learning/c_c++_call_test/c_call_c++ on git:main x [16:24:45]
$ g++ -c cal.cpp   

# dong @ ubuntu in ~/WorkSpace/Donge_Programs/Unix_Programming_Learning/c_c++_call_test/c_call_c++ on git:main x [16:24:52]
$ gcc -c main.c   

# dong @ ubuntu in ~/WorkSpace/Donge_Programs/Unix_Programming_Learning/c_c++_call_test/c_call_c++ on git:main x [16:24:56]
$ ls
cal.cpp  cal.h  cal.o  main.c  main.o下面看一下编译之后的中间文件cal.o和main.o的符号表,看看同一个函数embedded_art不同编译方式之后的差别。



image-20221219162704004

同样,不同的编译器处理方式不同,函数名称依旧不同!同样,需要加入extern "C"来告诉编译器按C的方式编译。
我们在cal.h的声明部分添加,然后重新编译!
extern "C" {
extern void embedded_art(void);
}



image-20221219163014548

可以看到符号表中,该函数名称正常,然后我们将中间文件链接起来。



image-20221219163536166

这个时候,会出现报错extern "C",这是什么情况?
在main.c文件中,引入了c++的头文件cal.h,因为"C"在C++编译的时候才能识别,C语言中并没有这个关键字。
所以,我们需要在g++编译的时候去加入extern "C",而gcc编译的时候跳过,这个时候就要提到c++编译时候的特定宏__cplusplus了,相当于一个阀门了。
我们修改cal.h文件
#ifdef __cplusplus
extern "C" {
#endif

extern void embedded_art(void);

#ifdef __cplusplus
}
#endif这样就确保了,c++编译embedded_art函数的时候,采用C语法编译,而gcc编译的时候,不作处理。
再次链接,执行!
# dong @ ubuntu in ~/WorkSpace/Donge_Programs/Unix_Programming_Learning/c_c++_call_test/c_call_c++ on git:main x [16:45:06] C:1
$ gcc -no-pie cal.o main.o -o main

# dong @ ubuntu in ~/WorkSpace/Donge_Programs/Unix_Programming_Learning/c_c++_call_test/c_call_c++ on git:main x [16:46:46]
$ ls
cal.cpp  cal.h  cal.o  main  main.c  main.o

# dong @ ubuntu in ~/WorkSpace/Donge_Programs/Unix_Programming_Learning/c_c++_call_test/c_call_c++ on git:main x [16:49:01]
$ ./main
main entry
嵌入式艺术5、总结

C/C++之间的相互调用,归根到底就是:不同的语言有不同的编译规则,要想实现通用,就必须告诉编译器,按照目标语言的规则进行编译!
回复

使用道具 举报

1

主题

9

帖子

15

积分

新手上路

Rank: 1

积分
15
发表于 2025-2-19 02:09:01 | 显示全部楼层
向楼主学习
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表