当前位置: 首页 > news >正文

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库中&#xff0c;我们经常可以还看下面这样的结构 #ifndef __TEST_H #define __TEST_H#ifdef _cplusplus extern "C" { #endif/*...*/#ifdef _cplusplus } #endif #endif#ifndef __TEST_H这样的宏定义应该是非常常见了&#xff0c;其作用是为了…...

python异常处理名称整理

Python 异常处理 python提供了两个非常重要的功能来处理python程序在运行中出现的异常和错误。你可以使用该功能来调试python程序。BaseException所有异常的基类UnboundLocalError访问未初始化的本地变量SystemExit...

SpringMVC拦截器

SpringMVC拦截器 介绍 拦截器&#xff08;interceptor&#xff09;的作用 SpringMVC的拦截器类似于Servlet开发中的过滤器Filter&#xff0c;用于对处理器 进行预处理和后处理 将拦截器按一定的顺序连接成一条链&#xff0c;这条链称为拦截器链&#xff08;Interception Ch…...

Python第八章作业(初级)

目录 第1关&#xff1a;统计字母数量 第2关&#xff1a;统计文章字符数 第3关&#xff1a;查询高校信息 第4关&#xff1a;查询高校名 第5关&#xff1a;通讯录读取 第6关&#xff1a;JSON转列表 第7关&#xff1a;利用数据文件统计成绩 第8关&#xff1a;研究生录取数据…...

chatgpt赋能python:Python中如何取消列表

Python中如何取消列表 在Python中使用列表是一种非常常见的数据结构&#xff0c;它允许我们在其中存储任意数量的元素&#xff0c;并且可以非常容易地进行遍历和操作。但是&#xff0c;有时候我们需要从列表中删除元素。这个过程并不难&#xff0c;但是有些细节需要注意。本文…...

Java中List排序的3种方法

在某些特殊的场景下&#xff0c;我们需要在 Java 程序中对 List 集合进行排序操作。比如从第三方接口中获取所有用户的列表&#xff0c;但列表默认是以用户编号从小到大进行排序的&#xff0c;而我们的系统需要按照用户的年龄从大到小进行排序&#xff0c;这个时候&#xff0c;…...

flutter-读写二进制文件到设备

看了下很多文章&#xff0c;本地文件存储都只有存储txt文件&#xff0c;我们探索下存储二进制文件吧。 保存二进制文件到设备硬盘上。 我们保存一个图片到手机本地上&#xff0c;并读取展示图片到app上。 以百度logo图为例子 写入图片 逻辑如下&#xff1a; 获取本地路径 -&g…...

C语言基础知识:内存分配

目录 内存分配原理 内存分配方法 静态内存分配 动态内存分配 MALLOC() CALLOC() 内存释放 注意事项 在C语言中&#xff0c;内存分配是非常重要的一个概念&#xff0c;因为C语言中没有内置的垃圾回收机制&#xff0c;需要我们手动管理内存的分配和释放。下面我们来详细讲…...

【Simulink】示波器图形数据导入Matlab重新绘图(论文)

版本&#xff1a;Matlab2019b 效果 示波器波形图片&#xff1a; 黑色背景&#xff0c;而且坐标轴字体较小&#xff0c;不方便修改&#xff0c;不能直接用在论文上面 对比 Matlab 绘图&#xff1a; 接下来介绍如何设置~ Simulink 设置 选择需要导入的示波器数据 点击 Vi…...

汇编调试及学习

汇编调试 打印寄存器的值 打印内存地址 打印8字节&#xff0c;就是64位 打印格式 是从低位取过来的 b 字节 h 双字节 w四字节 g八字节 前变基 后变基 。 后变基这个变基会发生变化的。前变基变基不会发生变化需要用&#xff01;号。 前变基 &#xff0c; 加了&#xff0…...

Linux - 第19节 - 网络基础(传输层二)

1.TCP相关实验 1.1.理解listen的第二个参数 在编写TCP套接字的服务器代码时&#xff0c;在进行了套接字的创建和绑定之后&#xff0c;需要调用listen函数将创建的套接字设置为监听状态&#xff0c;此后服务器就可以调用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 版本现已上线&#xff01;该版本支持新的 SpreadJS .sjs 文件格式和 Excel 模板文件 .xltx 格式。此外&#xff0c;GcExcel 支持更多的SpreadJS兼容性功能和对 GcDataViewer 的多项增强。看看下面的主要亮点。 导入/导出 Spread…...

面试官:MySQL自增主键一定是连续的吗?

测试环境&#xff1a; MySQL版本&#xff1a;8.0 数据库表&#xff1a;T &#xff08;主键id&#xff0c;唯一索引c&#xff0c;普通字段d&#xff09; 如果你的业务设计依赖于自增主键的连续性&#xff0c;这个设计假设自增主键是连续的。但实际上&#xff0c;这样的假设是错的…...

2023ACP世界大赛教育者论坛:让职业教育直面AI机遇与挑战

“AI技术的普及对创意行业和教育带来的影响和变革-2023 Adobe Certified Professional教育者论坛”在苏州西交利物浦大学成功举办。 本次论坛&#xff0c;由Adobe Certified Professional 世界大赛中国赛区组委会主办&#xff0c;联动了来自院校、海内外杰出的创意公司及国际知…...

Unity基础 音频组件以及音频播放

在游戏开发中&#xff0c;声音是一个重要的环节。Unity中的声音组件可以帮助开发者轻松地控制游戏中音频的播放、音量、循环等属性&#xff0c;从而实现更好的游戏体验。本文将详细介绍Unity声音组件的相关概念和技术&#xff0c;以及其在游戏、影视等领域的广泛应用和发展前景…...

SAP-MM-采购申请审批那些事!

1、ME55不能审批删除行项目的PR 采购申请审批可以设置行项目审批或抬头审批。如果设置为抬头审批时&#xff0c;ME55集中审批时&#xff0c;就会发现有些采购申请时不能审批的&#xff0c; 那么这些采购申请时真的不需要审批么&#xff1f;不是的&#xff0c;经过核对这些采购申…...

专业解读财务共享实现财务数智化转型的有效路径

近年来&#xff0c;随着数字经济的飞速发展&#xff0c;各大企业全面开启数智化转型之路&#xff0c;作为企业数智化转型的重要内容&#xff0c;财务数智化转型始于财务共享服务。然而&#xff0c;财务共享建设并不是一蹴而就的&#xff0c;如何通过财务共享实现财务数智化转型…...

九章云极DataCanvas公司诚邀您共享AI基础软件前沿技术盛宴

“杭州通用人工智能论坛暨AIIA人工智能产业发展大会”将于2023年5月30日-31日在杭州举办。本次人工智能产业发展大会由中国信息通信研究院、中国人工智能产业发展联盟主办&#xff0c;杭州城西科创大走廊管委会、杭州市经济和信息化局、杭州未来科技城管理委员会、人工智能关键…...

【高级语言程序设计(一)】第 10 章:文件

目录 一、文件概述 &#xff08;1&#xff09;文件定义 &#xff08;2&#xff09;文件命名 &#xff08;3&#xff09;文件分类 ① 按照文件的内容划分 ② 按照文件的组织形式划分 ③ 按照文件的存储形式划分 ④ 按照文件的存储介质划分 &#xff08;4&#xff09;文…...

C#与Unity的协作协议:从语法表层到引擎契约的深度解析

1. 这不是“学编程”&#xff0c;而是重新建立你和机器对话的语法系统很多人点开这个标题&#xff0c;心里想的是&#xff1a;“Unity游戏开发&#xff1f;那我得先学会C#&#xff0c;再学Unity编辑器&#xff0c;最后做个小飞机打砖块……”——这个思路本身就把门关死了。我带…...

P1311 选择客栈【洛谷算法习题】

P1311 选择客栈 网页链接 P1311 选择客栈 题目描述 丽江河边有 nnn 家很有特色的客栈&#xff0c;客栈按照其位置顺序从 111 到 nnn 编号。每家客栈都按照某一种色调进行装饰&#xff08;总共 kkk 种&#xff0c;用整数 0∼k−10 \sim k-10∼k−1 表示&#xff09;&#x…...

基于Java的外卖点餐配送系统_43lq510m

目录 同行可拿货,招校园代理 ,本人源头供货商项目概述技术栈核心功能模块项目亮点部署方式学习价值 项目技术支持获取博主联系方式 源码获取详细视频演示 &#xff1a;同行可合作点击我获取源码->获取博主联系方式->进我个人主页--> 同行可拿货,招校园代理 ,本人源头供…...

18分钟攻破GitHub:TeamPCP供应链攻击全技术解析与防御新范式

摘要 2026年5月18日&#xff0c;威胁组织TeamPCP通过一条精心设计的多级供应链攻击链&#xff0c;仅用18分钟就成功入侵全球最大代码托管平台GitHub的内部系统&#xff0c;窃取了约3800个核心私有仓库&#xff0c;涵盖Copilot、CodeQL、GitHub Actions等所有关键产品的源代码。…...

如何高效下载B站视频:Python开源工具bilibili-downloader完全指南

如何高效下载B站视频&#xff1a;Python开源工具bilibili-downloader完全指南 【免费下载链接】bilibili-downloader B站视频下载&#xff0c;支持下载大会员清晰度4K&#xff0c;持续更新中 项目地址: https://gitcode.com/gh_mirrors/bil/bilibili-downloader B站视频…...

Forza Painter终极指南:3分钟将任何图片变成专业车辆涂装

Forza Painter终极指南&#xff1a;3分钟将任何图片变成专业车辆涂装 【免费下载链接】forza-painter Import images into Forza 项目地址: https://gitcode.com/gh_mirrors/fo/forza-painter 还在为《极限竞速&#xff1a;地平线》系列游戏中复杂的车辆涂装设计而烦恼吗…...

微服务限流实战:Nginx 漏桶与网关令牌桶

限流不是为了让系统“变慢”&#xff0c;而是为了让系统在突发流量、恶意请求或超过承载能力时&#xff0c;仍然能保住核心服务。 一句话概括&#xff1a;限流是在入口处控制请求速度或并发数量&#xff0c;Nginx 常用漏桶算法控制请求流出速率&#xff0c;Spring Cloud Gatewa…...

OpenRocket:开源火箭设计与飞行仿真的终极指南

OpenRocket&#xff1a;开源火箭设计与飞行仿真的终极指南 【免费下载链接】openrocket Model-rocketry aerodynamics and trajectory simulation software 项目地址: https://gitcode.com/GitHub_Trending/op/openrocket 你是否曾经梦想设计自己的火箭&#xff0c;但又…...

终极指南:如何为ZXing创建企业级自定义解码器解决业务条码难题

终极指南&#xff1a;如何为ZXing创建企业级自定义解码器解决业务条码难题 【免费下载链接】zxing ZXing ("Zebra Crossing") barcode scanning library for Java, Android 项目地址: https://gitcode.com/gh_mirrors/zx/zxing 你是否曾遇到过标准扫码库无法解…...

零售业的AI Agent:个性化推荐与库存管理

从零落地零售业AI Agent:打通个性化推荐与智能库存管理的全链路实践 副标题:技术栈:LangChain + 向量数据库 + 时序预测 + 多Agent协同,降本提效30%+的可落地方案 第一部分:引言与基础 1.1 摘要/引言 不知道你有没有过这样的体验:刚在电商平台买了一罐婴儿奶粉,接下来…...