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

news/2024/10/20 3:52:57/

往期地址:

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

本期主题:
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 <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.cgcc -c main_c.cgcc -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 <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.cg++ -c main.cppg++ -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函数

#ifndef __FUNC_H__
#define __FUNC_H__
#include <stdio.h>#ifdef __cplusplus
extern "C" {
#endifvoid func();
#ifdef __cplusplus
}
#endif#endif

再次编译测试,能够正常匹配了:
在这里插入图片描述


http://www.ppmy.cn/news/44056.html

相关文章

Java面试题总结 | Java基础部分2(持续更新)

文章目录反射的优缺点BIO、AIO、NIO同步异步概念**阻塞与非阻塞****BIO****NIO****AIO**总结设计模式的好处设计模式一定是好用的吗Integer.ValueOf和new Integer的区别Integer.parseInt(s)与Integer.valueOf(s)的区别String是线程安全的吗&#xff1f;StringBuffer和StringBui…

统信UOS专业版系统安装教程 - 手动分区安装UOS系统

全文导读&#xff1a;本文主要介绍了安装UOS系统过程中使用手动分区安装方法&#xff0c;一般没有特殊要求建议使用全盘安装UOS系统。 准备环境 制作好统信UOS专业版启动U盘 一台CPU频率≥2GHz、内存≥4GB、硬盘≥64GB的电脑 安装步骤 一、制作UOS 系统启动盘 制作UOS 系…

NumPy 秘籍中文第二版:六、特殊数组和通用函数

原文&#xff1a;NumPy Cookbook - Second Edition 协议&#xff1a;CC BY-NC-SA 4.0 译者&#xff1a;飞龙 在本章中&#xff0c;我们将介绍以下秘籍&#xff1a; 创建通用函数查找勾股三元组用chararray执行字符串操作创建一个遮罩数组忽略负值和极值使用recarray函数创建一…

Qmake常用规则

当使用Qt进行开发时&#xff0c;qmake是一个非常常见的构建工具&#xff0c;可以通过一个简单的文本文件(.pro)来自动生成Makefiles文件&#xff0c;并将您需要的库和依赖项连接到最终生成的二进制文件中。以下是一些常见的、适用于初级程序员的qmake规则: SOURCES&#xff1a…

车企跨界背后,智能手机进入新增长时代

2022年中国智能手机销量首次跌破了3亿部大关&#xff0c;创下了十年来的最低水平。与此同时&#xff0c;以新能源车和XR为代表的新形态终端日渐成为新宠。在行业内外部多重因素影响之下&#xff0c;“唱衰”智能手机的声音也在甚嚣尘上&#xff0c;甚至有不少人认为智能机已进入…

五款数字孪生软件大比拼:优缺点分析测评报告

数字孪生&#xff08;Digital Twin&#xff09;作为当前最为火热的技术之一&#xff0c;已经被广泛应用于各种领域&#xff0c;包括航空、交通、能源、医疗、智能制造等行业。在数字孪生的实现中&#xff0c;数字孪生软件起到了重要的作用&#xff0c;因此市面上出现了很多数字…

C# log4net自动删除日志文件实现

log4net自动删除日志文件 1、用下边脚步保存成ClearLogs.bat。 :: 清理日志文件 echo off title 清理日志文件 :: 日志文件目录 set log_dir"D:\Logs\Weixin" :: 保留日志天数 set bak_dat10 ::删除空目录 for /f "delims" %%d in (dir "%log_di…

Baklib在线知识库/帮助中心:让客户服务更简单

随着互联网的快速发展&#xff0c;越来越多的企业开始意识到客户服务的重要性。然而&#xff0c;如何提供高效、可靠、贴心的客户服务&#xff0c;一直是企业面临的难题。在这个背景下&#xff0c;Baklib在线知识库/帮助中心应运而生。 什么是Baklib在线知识库/帮助中心 Bakl…