从编译角度看c和c++混合编译

往期地址:

  • 操作系统系列一 —— 操作系统概述
  • 操作系统系列二 —— 进程
  • 操作系统系列三 —— 编译与链接关系
  • 操作系统系列四 —— 栈与函数调用关系
  • 操作系统系列五——目标文件详解
  • 操作系统系列六 —— 详细解释【静态链接】

本期主题:
c和c++混合编译


C和C++混合编译

  • 1.回顾编译和链接
  • 2.简单例子使用gcc和ld
    • 1.gcc
    • 2.ld存在的问题(//TODO)
  • 3.c和c++混合编译
    • 1.使用nm看符号
    • 2.如何混合编译,extern "C"


1.回顾编译和链接

参考前面文章的链接,编译与链接 ,我们知道构建一共有4步:

  1. 预编译
  2. 编译
  3. 汇编
  4. 链接

我们常说的编译和链接,实际上编译包括了前面三步,即 预编译、编译和汇编

2.简单例子使用gcc和ld

写一个简单的例子,就是两个文件都是c文件,一个定义了func函数,另外一个main函数去调用func函数;

//func.h
#ifndef __FUNC_H__
#define __FUNC_H__
#include 
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
  1. gcc -c 是将xx.c文件生成目标文件,所谓目标文件的定义参考 操作系统系列五——目标文件详解
  2. 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 
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

编译结果:
从编译角度看c和c++混合编译_第1张图片
提示找不到func函数,这是为什么呢?

1.使用nm看符号

我们来定位上面的问题,我们将编译生成的目标文件进行符号解析看看,我们使用nm命令,nm命令能够将目标文件中的symbols(即符号信息)都展示出来
nm命令:
从编译角度看c和c++混合编译_第2张图片看 main.o 以及 func.o的符号信息:
从编译角度看c和c++混合编译_第3张图片一看就发现了问题, 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函数

#ifndef __FUNC_H__
#define __FUNC_H__
#include 

#ifdef __cplusplus
extern "C" {
#endif
	void func();
#ifdef __cplusplus
}
#endif

#endif

再次编译测试,能够正常匹配了:
从编译角度看c和c++混合编译_第4张图片

你可能感兴趣的:(计算机操作系统,c++,c语言,算法)