【C++入门】C和C++混合编程超详细讲解_c++编程-程序员宅基地

技术标签: C++  c++  c语言  开发语言  

1、为什么需要C和C++混合编程

(1)C++是在C语言的基础上发展出来的,在C++发明之前已经用C语言实现了很多功能库,C++要使用这些库就涉及到调用C语言;
(2)C++和C语言各自有更适合的领域,在大型项目中都需要使用,涉及到C和C++的互相调用。比如嵌入式设备开发中,应用层开发、算法开发中使用C++,底层的操作系统、音视频开发使用C语言;

2、为什么不同的语言可以混合编程

(1)程序编译的过程:高级语言->汇编语言->二进制代码->链接->可执行程序->烧录镜像;
(2)任何语言最终都要变成二进制的可执行程序才能在CPU上运行,不同的高级语言在编译成汇编语言的过程是不一样的,所以每种高级语言都有自己的编译器;
(3)不管哪种高级语言在汇编语言阶段或者二进制代码阶段时都是一样的格式,互相之间是可以调用的,也就是我们常看到的库文件,开发中常把代码编译成库供其他人调用;
(4)高级语言的目标都是编译成汇编指令,汇编指令是和CPU相关而与高级语言无关,所以CPU相同的情况下,每种高级语言在汇编阶段都是统一的;

3、C和C++混合编程的困难

3.1、C++的函数重载带来的麻烦

(1)C和C++之间互相调用是根据函数名进行,在链接时通过函数名将函数对应的二进制代码链接起来;
(2)C++支持函数重载:C++中可以有相同名字的函数,只要同名函数的传参不同,C++就不会报错,并根据调用时的传参来选择调用对应的函数;
(3)在符号表中,C语言的函数名是不考虑传参的,但是C++的函数名是和传参类型有关的,这就导致通一个函数在C和C++中函数名不一致,也就会导致在链接时找不到函数的报错;

3.2、验证C++的函数重载机制

3.2.1、C++代码

//cppTest.cpp

int add(int a, int b)
{
    
	
	return a+b;
	
}

float add(float a, float b)
{
    
	return a+b;
}

3.2.2、C代码

//cTest.c

int add(int a, int b)
{
    
	
	return a+b;
	
}

3.2.3、编译成汇编代码

//得到c代码对应的汇编代码
gcc -c cTest.c -o cTest.o
objdump -d cTest.o > cTest.i

//得到c++代码对应的汇编代码
g++ -c cppTest.c -o cppTest.o
objdump -d cppTest.o > cppTest.i

3.2.4、实验结果分析

在这里插入图片描述

(1)C++支持函数重载,所以可以在C++代码中定义两个同名但传参不同的函数,函数名分别叫_Z3addii和_Z3addff,其中最后两个字母和传参有关,ii表示两个传参都是int型,ff表示两个传参都是float型;
(2)通过对比,在C和C++中int add(int a, int b)函数的汇编代码都是一样的,不同的是函数名称,在C语言中是add,在C++中是_Z3addii;
(3)经过实现可知,如果不经过任何处理,虽然C和C++都定义了同样的add函数,但是经过编译后得到的汇编代码中函数名称却不同,这会导致链接时找不到函数;

4、解决函数重载带来的不兼容性

4.1、解决思路

(1)函数重载机制是C++的特性,而C语言并没有,根据向前兼容原则,需要C++去兼容C,而不能要求C语言去支持函数重载机制;
(2)解决方法就是C++在需要和C对接的局部不采用函数重载机制,向C兼容;
(3)在C++中,用extern “C”{}括起来的内容表示向C兼容,不要使用函数重载机制;
注意点:extern “C”{}是C++中支持的,在C中是没有extern “C”{}这个用法的,C语言使用编译会报错;

4.2、extern “C”{}使用示例

#ifdef __cplusplus
extern "C"{
    
#endif

	······

#ifdef __cplusplus
}
#endif

(1)__cplusplus是C++中的宏,表示当前是C++的编译环境;
(2)上面实现的效果就是在C++的编译环境中就使用extern “C”{}向C语言兼容,如果不是C++的编译环境就不使用extern “C”{};

5、混合编程的情况

5.1、C++调用C:C是库

5.1.、C函数代码:cTest.c

int add(int a, int b)
{
    
	
	return a+b;
	
}

5.1.2、C头文件:cTest.h

#ifndef __CTEST_H__
#define __CTEST_H__

#ifdef __cplusplus
extern "C"{
    
#endif

int add(int a, int b);

#ifdef __cplusplus
}
#endif

#endif

5.1.3、C++调用代码:cppTest.cpp

#include <iostream>
#include "cTest.h" 

using namespace std;

int main()
{
    
	int a = 1;
	int b= 2;
	
	cout << add(a, b) << endl;
	
	return 0;
}

5.1.4、编译链接代码

5.1.4.1使用extern “C”{}

[root#]$ ls
cppTest.cpp  cTest.c  cTest.h  
[root#]$ 
[root#]$ gcc -c cTest.c -o cTest.o
[root#]$ 
[root#]$ ar -r libcTest.a cTest.o	
ar: creating libcTest.a
[root#]$ 
[root#]$ g++ cppTest.cpp -L. -lcTest
[root#]$ 
[root#]$ ls
a.out  cppTest.cpp  cTest.c  cTest.h  cTest.o  libcTest.a
[root#]$ 
[root#]$ ./a.out 
3

5.1.4.2、不使用extern “C”{}

root#]$  g++ cppTest.cpp -L. -lcTest
/tmp/cc0Wckzc.o: In function `main':
cppTest.cpp:(.text+0x21): undefined reference to `add(int, int)'
collect2: ld returned 1 exit status

把cTest.h头文件中的extern “C”{}去掉,按照上面的步骤重新再编译执行一遍,会报add函数未定义的错误;

5.1.5、实验现象分析

root#]$ nm libcTest.a 
cTest.o:
0000000000000000 T add

(1)cTest.h头文件中没有extern “C”{}去修饰,则会按照函数重载机制去调用add函数,也就是按照_Z3addii符号去libcTest.a中查找函数;
(2)用nm命令可以看到,在链接得到的libcTest.a库中只有add名字的函数,所以会报add函数未定义的错误;

5.1.6、总结

用C语言写功能库代码时,需要对外提供的函数接口头文件用extern “C”{}括起来,将来库无论被C语言调用还是被C++调用都支持;

5.2、C调用C++:C++是库

5.2.1、C++函数代码:cppTest.cpp

#include "cppTest.hpp"

int add(int a, int b)
{
    
	
	return a+b;
	
}

5.2.2、C++头文件:cppTest.hpp

#ifndef __CPPTEST_HPP__
#define __CPPTEST_HPP__

int add(int a, int b);

#endif

5.2.3、C调用代码:cTest.c

#include <stdio.h>
#include "cppTest.hpp" 

int main()
{
    
	int a = 1;
	int b= 2;
	
	printf("a+b=%d\n", add(a, b));
	
	return 0;
}

5.2.4、编译链接代码

[root#]$ g++ cppTest.cpp -c -o cppTest.o
[root#]$ 
[root#]$ ar -r libcppTest.a cppTest.o
ar: creating libcppTest.a
[root#]$ 
[root#]$ gcc cTest.c -L ./ -lcppTest
/tmp/cchDGkJK.o: In function `main':
cTest.c:(.text+0x21): undefined reference to `add'
collect2: ld returned 1 exit status
[root#]$ 
[root#]$ nm libcppTest.a 

cppTest.o:
0000000000000000 T _Z3addii
                 U __gxx_personality_v0

(1)在编译C语言的可执行程序时,报add未定义的错误,因为在C++实现的libcppTest.a库中并没有用extern “C”{}将对C提供的add函数括起来,这样在编译
add函数时就会用函数重载机制,最终add函数在符号表中是_Z3addii,C语言按照add符号去链接时就会找不到add函数;
(2)冷知识:如果C语言中,按照_Z3addii函数名去调用,是可以成功的;

5.3、解决方案一:直接改C++源码

5.3.1、C++头文件增加extern “C”{}

#ifndef __CPPTEST_H__
#define __CPPTEST_H__

#ifdef __cplusplus
extern "C"{
    
#endif

int add(int a, int b);

#ifdef __cplusplus
}
#endif

#endif

5.3.2、编译链接得到可执行程序

[root#]$g++ cppTest.cpp -c -o cppTest.o
[root#]$
[root#]$ar -r libcppTest.a cppTest.o
[root#]$
[root#]$gcc cTest.c -L. -lcppTest -lstdc++
[root#]$
[root#]$ ./a.out 
a+b=3
[root#]$ nm libcppTest.a 

cppTest.o:
                 U __gxx_personality_v0
0000000000000000 T add

修改C++库的代码,将要被C调用的函数头文件用extern “C”{}括起来,兼容C的调用;

5.4、解决方案二:将C++库再封装一层

5.4.1、使用场景

(1)如果别人给你提供了C++写的代码库,但是写C++代码库的人并没有考虑被C调用的情况,所以给你的库版本并没有用extern “C”{}去兼容C调用;
(2)通常对方提供.so动态库,你在没有源码的情况下是无法修改源码并重新编译动态库的,但是你可以对动态库再封装一层,封装的接口用extern “C”{}去括起来;

5.4.2、封装的C++源文件:cppPack.cpp

#include "cppPack.hpp"
#include "cppTest.hpp"

int addPack(int a, int b)
{
    
	
	return add(a, b);
	
}

5.4.3、封装的C++头文件:cppPack.hpp

#ifndef __CPPPACK_HPP__
#define __CPPPACK_HPP__

#ifdef __cplusplus
extern "C"{
    
#endif

int addPack(int a, int b);

#ifdef __cplusplus
}
#endif

#endif

5.4.4、C调用代码:cTest

#include <stdio.h>
#include "cppPack.hpp" 

int main()
{
    
	int a = 1;
	int b= 2;
	
	//这里调用封装的addPack函数,addPack函数内部就是调用的add函数
	printf("a+b=%d\n", addPack(a, b));
	return 0;
}

5.4.4、编译链接代码

root@ubuntu:# g++ cppPack.cpp -c -o cppPack.o
root@ubuntu:# ar -r libcppPack.a cppPack.o
root@ubuntu:# gcc cTest.c -L ./ -lcppPack -lcppTest -I ./
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_42031299/article/details/126688788

智能推荐

python服务器端开发面试_【网易游戏Python面试】python 服务端开发-看准网-程序员宅基地

文章浏览阅读145次。10.21终面已参加,希望能顺利通过终面拿到offer~一共三轮,电话面试+笔试+视频面试,视频面试3V110月19日投的新媒体运营的简历,HR说因为是周末,等工作日再联系我,在周一下午三点我接到了电话成功通过简历筛选和电话面试,整个电话面试的过程长,大概10分钟左右,因为前期稍微做了一些准备,所以还算对答如流,整个过程顺利,HR现场告诉我通过面试,并随即给我发了笔试题,让我准备一下,最晚三天之..._网易 python游戏服务器

MVC层次划分简述_mvc分层-程序员宅基地

文章浏览阅读6.5k次,点赞12次,收藏38次。MVC层次划分简述写在前面的一段话:首先要知道MVC和三层架构之间有什么关系:MVC:【 Model(数据模型) - View(视图) - Controller(控制器) 】三层架构:【 Presentation tier(展现层) - Application tier(应用层)+Date tier(数据访问层) 】很多人都有一个误解,认为Spring MVC的M、V、C对..._mvc分层

Flink的sink实战之三:cassandra3_flink cassandra-程序员宅基地

文章浏览阅读2.9k次。实践flink数据集sink到cassandra3_flink cassandra

使用docker安装codimd,搭建你自己的在线协作markdown编辑器_群晖 docker 搭建 codimd-程序员宅基地

文章浏览阅读7.1k次,点赞4次,收藏12次。文章目录一、前言二、codimd是什么?2.1 源于hackmd的超好用markdown编辑器2.2 codimd的作用三、安装和使用3.1 安装前需要知道的3.2 安装步骤3.2.1 创建数据库3.2.2 安装git3.2.3 安装docker3.2.4 安装docker compose3.2.5 安装codimd3.2.6 检查是否安装成功3.2.7 放行端口3.2.8 测试使用3.3 开始写..._群晖 docker 搭建 codimd

Json和ajax-程序员宅基地

文章浏览阅读335次。Json json 可以定义多种类型 var jsonObj = { "key1":123, "key2":"name", "key3":[12,"age",true], //数组 "key4":false, "key5":{ //存一个json对象 "key6":456, "key7":"number" }} json其实就是一个Object对象, 他的key值 可以看成对象的一个属性, 获取他的value值...

ssm超市账单管理系统a2e96【独家源码】 应对计算机毕业设计困难的解决方案-程序员宅基地

文章浏览阅读87次。选题背景:超市账单管理系统是一种针对超市行业的管理工具,旨在提供高效、准确、便捷的账单管理服务。随着城市化进程的加快和人们生活水平的提高,超市作为日常生活必需品的主要供应渠道之一,扮演着重要的角色。然而,传统的超市账单管理方式存在一些问题,如手工记录容易出错、数据整理繁琐、信息不透明等。因此,开发一个科技化的超市账单管理系统成为了必要之举。选题意义:首先,超市账单管理系统的开发可以提高账单管理的效率。传统的超市账单管理方式通常需要员工手动记录商品销售信息,并进行数据整理和汇总。这种方式容易出现人为错

随便推点

bookmarks_2021_9_28_拾度智能科技 att7022eu-程序员宅基地

文章浏览阅读1.7k次。书签栏通讯 s7-1200与s7-200smart通讯-工业支持中心-西门子中国IO_deviceS7-1200PROFINET通信ET 200SP 安装视频 - ID: 95886218 - Industry Support Siemens云平台接入在线文档 - 低代码开发嵌入式设备 | 物一世 WareExpress在linux下使用c语言实现MQTT通信(一.MQTT原理介绍及流程图)_qq_44041062的博客-程序员宅基地C mqtt_百度搜索开发快M_拾度智能科技 att7022eu

国家取消职称英语与计算机,全国职称英语考试取消-程序员宅基地

文章浏览阅读1.6k次。职称英语全称为全国专业技术人员职称英语等级考试,是由国家人事部组织实施的一项国家级外语考试。1.概述全国专业技术人员职称英语等级考试是由人力资源和社会保障部组织实施的一项外语考试,它根据英语在不同专业领域活动中的应用特点,结合专业技术人员掌握和应用英语的实际情况,对申报不同级别职称的专业技术人员的英语水平提出了不同的要求。该考试根据专业技术人员使用英语的实际情况,把考试的重点放在了阅读理解上面。全..._全国专业技术人员职称英语等级考试 北京 取消

where里能用max吗_网络里能找到真爱吗?-程序员宅基地

文章浏览阅读42次。恋爱指导篇 知心的小爱“真爱”是一个永不过时的话题,古代的人找对象,靠的是媒妁之言,父母定婚姻。现代的人靠的是相亲,自由恋爱,按理找一个喜欢的人结婚会很幸福,近几年反而离率更高了。古代人认识的人少,交流工具少,最多信鸽传书,信物传情。现代要认识一个人很容易了,最初是电话信息联系。前几年是qq,微信摇一摇,近两年是抖音,快手随便找一找。虽然找对象,寻伴侣更方便了,为何大部分人还是感觉更迷茫,不快乐...

刷题记录第八十天-修剪二叉搜索树-程序员宅基地

文章浏览阅读109次。【代码】刷题记录第八十天-修剪二叉搜索树。

dcm4che,WADO相关-程序员宅基地

文章浏览阅读248次。关于 dcm4che WADO WADO:Web Access to DICOM Objects dcm4che 是一个为医疗保健企业的开源应用程序和工具集合。这些应用程序已经开发了Java编程语言的性能和便携性,在JDK 1.6及更高版本支持部署。在dcm4che项目的核心是一个强大的执行DICOM标准的。该dcm4che-1.x和dcm4che-2.X DICOM Tool..._dcm4che实现wado服务

linux查看zk日志,14.1 zookeeper日志查看-程序员宅基地

文章浏览阅读2.2k次。zookeeper服务器会产生三类日志:事务日志、快照日志和log4j日志。在zookeeper默认配置文件zoo.cfg(可以修改文件名)中有一个配置项dataDir,该配置项用于配置zookeeper快照日志和事务日志的存储地址。在官方提供的默认参考配置文件zoo_sample.cfg中,只有dataDir配置项。其实在实际应用中,还可以为事务日志专门配置存储地址,配置项名称为dataLogD..._linux查看zookeeper日志

推荐文章

热门文章

相关标签