c++中的extern “C“
在一些c语言的library库中,我们经常可以还看下面这样的结构
#ifndef __TEST_H
#define __TEST_H#ifdef _cplusplus
extern "C" {
#endif/*...*/#ifdef _cplusplus
}
#endif
#endif
#ifndef __TEST_H这样的宏定义应该是非常常见了,其作用是为了避免重复包含。
往下看,如果定义了_cplusplus宏,则添加extern "C"的标记,那么这个标记的作用是什么呢?
#ifdef _cplusplus
extern "C" {
#endif
这里首先给出答案,这是为了c/c++程序可以相互调用。下面就看看extern "C"是如何做到的。我们分两个场景,第一个场景就是c语言写的库,c和c++程序去调用。第二个场景就是c++写的库,c和c++程序去调用。
c写的库给c/c++调用
我们看第一个例子,在这个例子中,我们使用c语言构建了一个add函数,并提供了其头文件。我们要将该实现提供给c和c++的程序调用。
下面是该例子的目录结构。
.
├── add.c
├── add.h
├── main_c.c
├── main_cpp.cpp
└── makefile
add.h
#ifndef C_EXAMPLE_H
#define C_EXAMPLE_H#ifdef __cplusplus
extern "C"{
#endifextern int add(int x,int y);#ifdef __cplusplus
}
#endif#endif
add.c
#include "add.h"
int add( int x, int y )
{return x + y;
}
main_cpp.cpp
#include "add.h"int main()
{add(2,3);return 0;
}
main_c.c
#include "add.h"int main()
{add(2,3);return 0;
}
makefile
all:main_cpp main_cmain_cpp: main_cpp.o add.o$(CXX) -o $@ $^main_c: main_c.o add.o$(CC) -o $@ $^main.o: main_cpp.cpp$(CXX) -c -o $@ $<add.o: add.c$(CC) -c -o $@ $<clean:rm -f *.o main_cpp main_c
使用make命令对上述模块进行构建。如果没有任何错误,那么恭喜你,add.o成功的被c和c++程序使用了。
我们知道c和c++编译器编译出来的符号名称是不同的,用c++的方式去寻找c语言的符号是无法寻找到的。extern "C"为何可以做到?
我们使用readelf -s add.o查看add.o的符号,可以看到add函数的名称就是add,这个就是典型c编译器编译出来的名字。
Symbol table '.symtab' contains 9 entries:Num: Value Size Type Bind Vis Ndx Name0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND1: 0000000000000000 0 FILE LOCAL DEFAULT ABS add.c2: 0000000000000000 0 SECTION LOCAL DEFAULT 13: 0000000000000000 0 SECTION LOCAL DEFAULT 24: 0000000000000000 0 SECTION LOCAL DEFAULT 35: 0000000000000000 0 SECTION LOCAL DEFAULT 56: 0000000000000000 0 SECTION LOCAL DEFAULT 67: 0000000000000000 0 SECTION LOCAL DEFAULT 48: 0000000000000000 20 FUNC GLOBAL DEFAULT 1 add
我们再次查看readelf -s main_cpp.o | grep add去查看一下main_cpp中的符号表:
72: 0000000000000000 0 FILE LOCAL DEFAULT ABS add.c90: 0000000000400570 20 FUNC GLOBAL DEFAULT 11 add
可以看到main_cpp中的符号表的名字也是add,因此main_cpp成功的找到了add函数。
但是我们知道c++的编译器在生成符号时,通常都会带上符号的参数类型(因为c++支持重载),例如下面的c++程序,编译之后,我们使用readelf查看符号表。
int add( int x, int y )
{return x + y;
}int main()
{add(2,3);return 0;
}
其输出的结果如下所示:
86: 0000000000400556 20 FUNC GLOBAL DEFAULT 11 _Z3addii
可以看到add生成符号是_Z3addii。
因此,加与不加extern "C",add函数生成的符号名称是不同的。
看到这里,聪明的你已经大概知道extern "C"的作用了,就是修改了符号表的生成方式,将c++符号的生成方式换成了c的生成方式。
c库中生成的符号是c编译器的符号, 因此c语言可以直接链接。而c++程序需要使用extern "C"让编译器使用c的符号命名方式去进行链接,这样才能找到对应的符号。
c++写的库给c/c++调用
下面这个例子,我们使用c++语言构建了一个add函数,并提供了其头文件。我们要将该实现提供给c和c++的程序调用。
.
├── add.cpp
├── add.h
├── main_c.c
├── main_cpp.cpp
└── makefile
add.h
#ifndef C_EXAMPLE_H
#define C_EXAMPLE_H#ifdef __cplusplus
extern "C"{
#endifextern int add(int x,int y);#ifdef __cplusplus
}
#endif#endif
add.cpp
#include "add.h"
int add( int x, int y )
{return x + y;
}
main_cpp.cpp
#include "add.h"int main()
{add(2,3);return 0;
}
main_c.c
#include "add.h"int main()
{add(2,3);return 0;
}
makefile
all:main_cpp main_cmain_cpp: main_cpp.o add.o$(CXX) -o $@ $^main_c: main_c.o add.o$(CC) -o $@ $^main.o: main_cpp.cpp$(CXX) -c -o $@ $<add.o: add.cpp$(CXX) -c -o $@ $<clean:rm -f *.o main_cpp main_c
其实这种场景和第一种场景是基本一致的。现在我们的库add.o是使用c++编译器生成的。
我们使用readelf查看其内容,可以看到其内容和之前c语言生成的库,add函数的符号是一样的。因为我们此时编译时使用了extern "C",也就是说使用c语言的符号构建方式进行编译。
Symbol table '.symtab' contains 9 entries:Num: Value Size Type Bind Vis Ndx Name0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND1: 0000000000000000 0 FILE LOCAL DEFAULT ABS add.c2: 0000000000000000 0 SECTION LOCAL DEFAULT 13: 0000000000000000 0 SECTION LOCAL DEFAULT 24: 0000000000000000 0 SECTION LOCAL DEFAULT 35: 0000000000000000 0 SECTION LOCAL DEFAULT 56: 0000000000000000 0 SECTION LOCAL DEFAULT 67: 0000000000000000 0 SECTION LOCAL DEFAULT 48: 0000000000000000 20 FUNC GLOBAL DEFAULT 1 add
接下来链接的过程就和第一个场景一样了。
在本场景中,使用c++编写了一个包含add函数的模块,对其编译时,使用了c语言的符号构建方式。因此其符号表和c语言的库是相同的。最终链接时,由于加上了extern "C", 链接过程也将使用c语言的方式去寻找符号。
总结
- extern "C"实际上就是告诉c++编译器去使用c编译器的规则去进行构建。
- 在日常使用中,c++调用c库的使用频率更高一些,案例一就是这样的例子。
相关文章:
c++中的extern “C“
在一些c语言的library库中,我们经常可以还看下面这样的结构 #ifndef __TEST_H #define __TEST_H#ifdef _cplusplus extern "C" { #endif/*...*/#ifdef _cplusplus } #endif #endif#ifndef __TEST_H这样的宏定义应该是非常常见了,其作用是为了…...
python异常处理名称整理
Python 异常处理 python提供了两个非常重要的功能来处理python程序在运行中出现的异常和错误。你可以使用该功能来调试python程序。BaseException所有异常的基类UnboundLocalError访问未初始化的本地变量SystemExit...
SpringMVC拦截器
SpringMVC拦截器 介绍 拦截器(interceptor)的作用 SpringMVC的拦截器类似于Servlet开发中的过滤器Filter,用于对处理器 进行预处理和后处理 将拦截器按一定的顺序连接成一条链,这条链称为拦截器链(Interception Ch…...
Python第八章作业(初级)
目录 第1关:统计字母数量 第2关:统计文章字符数 第3关:查询高校信息 第4关:查询高校名 第5关:通讯录读取 第6关:JSON转列表 第7关:利用数据文件统计成绩 第8关:研究生录取数据…...
chatgpt赋能python:Python中如何取消列表
Python中如何取消列表 在Python中使用列表是一种非常常见的数据结构,它允许我们在其中存储任意数量的元素,并且可以非常容易地进行遍历和操作。但是,有时候我们需要从列表中删除元素。这个过程并不难,但是有些细节需要注意。本文…...
Java中List排序的3种方法
在某些特殊的场景下,我们需要在 Java 程序中对 List 集合进行排序操作。比如从第三方接口中获取所有用户的列表,但列表默认是以用户编号从小到大进行排序的,而我们的系统需要按照用户的年龄从大到小进行排序,这个时候,…...
flutter-读写二进制文件到设备
看了下很多文章,本地文件存储都只有存储txt文件,我们探索下存储二进制文件吧。 保存二进制文件到设备硬盘上。 我们保存一个图片到手机本地上,并读取展示图片到app上。 以百度logo图为例子 写入图片 逻辑如下: 获取本地路径 -&g…...
C语言基础知识:内存分配
目录 内存分配原理 内存分配方法 静态内存分配 动态内存分配 MALLOC() CALLOC() 内存释放 注意事项 在C语言中,内存分配是非常重要的一个概念,因为C语言中没有内置的垃圾回收机制,需要我们手动管理内存的分配和释放。下面我们来详细讲…...
【Simulink】示波器图形数据导入Matlab重新绘图(论文)
版本:Matlab2019b 效果 示波器波形图片: 黑色背景,而且坐标轴字体较小,不方便修改,不能直接用在论文上面 对比 Matlab 绘图: 接下来介绍如何设置~ Simulink 设置 选择需要导入的示波器数据 点击 Vi…...
汇编调试及学习
汇编调试 打印寄存器的值 打印内存地址 打印8字节,就是64位 打印格式 是从低位取过来的 b 字节 h 双字节 w四字节 g八字节 前变基 后变基 。 后变基这个变基会发生变化的。前变基变基不会发生变化需要用!号。 前变基 , 加了࿰…...
Linux - 第19节 - 网络基础(传输层二)
1.TCP相关实验 1.1.理解listen的第二个参数 在编写TCP套接字的服务器代码时,在进行了套接字的创建和绑定之后,需要调用listen函数将创建的套接字设置为监听状态,此后服务器就可以调用accept函数获取建立好的连接了。其中listen函数的第一个参…...
web实现日历、阳历农历之间相互转换、npm、push、unshift、includes、innerHTML
文章目录 1、原生web实现效果图htmlJavaScriptstyle vue2实现htmlJavaScript 1、原生web实现 效果图 html <div class"box"><div class"week"><div>星期日</div><div>星期一</div><div>星期二</div><…...
GcExcel v6.1 支持新的 ‘.sjs‘ 模板文件 ‘.xltx‘ 格式 Crack
GrapeCity Documents for Excel (GcExcel) v6.1 版本现已上线!该版本支持新的 SpreadJS .sjs 文件格式和 Excel 模板文件 .xltx 格式。此外,GcExcel 支持更多的SpreadJS兼容性功能和对 GcDataViewer 的多项增强。看看下面的主要亮点。 导入/导出 Spread…...
面试官:MySQL自增主键一定是连续的吗?
测试环境: MySQL版本:8.0 数据库表:T (主键id,唯一索引c,普通字段d) 如果你的业务设计依赖于自增主键的连续性,这个设计假设自增主键是连续的。但实际上,这样的假设是错的…...
2023ACP世界大赛教育者论坛:让职业教育直面AI机遇与挑战
“AI技术的普及对创意行业和教育带来的影响和变革-2023 Adobe Certified Professional教育者论坛”在苏州西交利物浦大学成功举办。 本次论坛,由Adobe Certified Professional 世界大赛中国赛区组委会主办,联动了来自院校、海内外杰出的创意公司及国际知…...
Unity基础 音频组件以及音频播放
在游戏开发中,声音是一个重要的环节。Unity中的声音组件可以帮助开发者轻松地控制游戏中音频的播放、音量、循环等属性,从而实现更好的游戏体验。本文将详细介绍Unity声音组件的相关概念和技术,以及其在游戏、影视等领域的广泛应用和发展前景…...
SAP-MM-采购申请审批那些事!
1、ME55不能审批删除行项目的PR 采购申请审批可以设置行项目审批或抬头审批。如果设置为抬头审批时,ME55集中审批时,就会发现有些采购申请时不能审批的, 那么这些采购申请时真的不需要审批么?不是的,经过核对这些采购申…...
专业解读财务共享实现财务数智化转型的有效路径
近年来,随着数字经济的飞速发展,各大企业全面开启数智化转型之路,作为企业数智化转型的重要内容,财务数智化转型始于财务共享服务。然而,财务共享建设并不是一蹴而就的,如何通过财务共享实现财务数智化转型…...
九章云极DataCanvas公司诚邀您共享AI基础软件前沿技术盛宴
“杭州通用人工智能论坛暨AIIA人工智能产业发展大会”将于2023年5月30日-31日在杭州举办。本次人工智能产业发展大会由中国信息通信研究院、中国人工智能产业发展联盟主办,杭州城西科创大走廊管委会、杭州市经济和信息化局、杭州未来科技城管理委员会、人工智能关键…...
【高级语言程序设计(一)】第 10 章:文件
目录 一、文件概述 (1)文件定义 (2)文件命名 (3)文件分类 ① 按照文件的内容划分 ② 按照文件的组织形式划分 ③ 按照文件的存储形式划分 ④ 按照文件的存储介质划分 (4)文…...
SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...
微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...
ElasticSearch搜索引擎之倒排索引及其底层算法
文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...
IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...
SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...
排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...
LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》
这段 Python 代码是一个完整的 知识库数据库操作模块,用于对本地知识库系统中的知识库进行增删改查(CRUD)操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 📘 一、整体功能概述 该模块…...
