往期地址:
- 操作系统系列一 —— 操作系统概述
- 操作系统系列二 —— 进程
- 操作系统系列三 —— 编译与链接关系
- 操作系统系列四 —— 栈与函数调用关系
- 操作系统系列五——目标文件详解
- 操作系统系列六 —— 详细解释【静态链接】
本期主题:
c和c++混合编译
1.回顾编译和链接
参考前面文章的链接,编译与链接 ,我们知道构建一共有4步:
- 预编译
- 编译
- 汇编
- 链接
我们常说的编译和链接,实际上编译包括了前面三步,即 预编译、编译和汇编;
2.简单例子使用gcc和ld
写一个简单的例子,就是两个文件都是c文件,一个定义了func函数,另外一个main函数去调用func函数;
//func.h
#ifndef __FUNC_H__
#define __FUNC_H__
#include <stdio.h>
void func();
#endif
//func.c
#include "func.h"
void func(void)
{
printf("C code, hello world!\n");
}
//main_c.c
#include "func.h"
int main(void)
{
printf("main func\n");
func();
return 0;
}
1.gcc
使用gcc来编译和链接,makefile文件如下:
c_comp:
gcc -c func.c
gcc -c main_c.c
gcc -o a.out func.o main_c.o
clean:
rm *.o a.out
- gcc -c 是将xx.c文件生成目标文件,所谓目标文件的定义参考 操作系统系列五——目标文件详解
- gcc -o 是将目标文件链接成可执行文件
2.ld存在的问题(//TODO)
前面的例子如果用ld来进行链接,会存在问题:
问题是提示缺少了 puts 的reference信息,原因:
这是由于我们的函数用到了printf,printf的底层就是puts,printf依赖于libc.so,但是我们用ld链接时并未将libc.so链接进去
3.c和c++混合编译
将上面的例子改为cpp程序来调用c写好的接口:
//func.h
#ifndef __FUNC_H__
#define __FUNC_H__
#include <stdio.h>
void func();
#endif
//func.c
#include "func.h"
void func(void)
{
printf("C code, hello world!\n");
}
//main.cpp
#include "func.h"
int main(void)
{
printf("main func\n");
func();
return 0;
}
makefile如下:
cpp_comp:
gcc -c func.c
g++ -c main.cpp
g++ -o a.out func.o main.o
编译结果:
提示找不到func函数,这是为什么呢?
1.使用nm看符号
我们来定位上面的问题,我们将编译生成的目标文件进行符号解析看看,我们使用nm命令,nm命令能够将目标文件中的symbols(即符号信息)都展示出来
nm命令:
看 main.o 以及 func.o的符号信息:
一看就发现了问题, main.o中对func的符号要求和func.o中的符号要求不一致,所以导致找不到func符号信息
。
原因剖析:
对于cpp代码而言,函数有重载的可能性,所以cpp编译的目标文件中,对于函数符号是会有参数信息的,例如 funcv实际上就是func(void)的意思
这也就意味着,使用 C 和 C++ 进行混合编程时,考虑到对函数名的处理方式不同,势必会造成编译器在程序链接阶段无法找到函数具体的实现,导致链接失败。
2.如何混合编译,extern “C”
c++已经考虑到了这个问题,所以提供了一个 extern “C” 的方案,作用是:
extern “C” 既可以修饰一句 C++ 代码,也可以修饰一段 C++ 代码,它的功能是让编译器以处理 C 语言代码的方式来处理修饰的 C++ 代码。
前面我们那个例子,
- 在main.cpp中包含了func.h头文件,main.cpp会以c++的方式来解析func函数
- 而func.c中又是以c的方式来解析func函数
因此解决方案就是在func.h文件中添加extern C,告诉main.cpp同样以c的方式来解析func函数文章来源:https://www.toymoban.com/news/detail-408347.html
#ifndef __FUNC_H__
#define __FUNC_H__
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
void func();
#ifdef __cplusplus
}
#endif
#endif
再次编译测试,能够正常匹配了:
文章来源地址https://www.toymoban.com/news/detail-408347.html
到了这里,关于从编译角度看c和c++混合编译的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!