当前位置: 首页 > 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;文…...

生成xcframework

打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式&#xff0c;可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

springboot 百货中心供应链管理系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;百货中心供应链管理系统被用户普遍使用&#xff0c;为方…...

【JavaEE】-- HTTP

1. HTTP是什么&#xff1f; HTTP&#xff08;全称为"超文本传输协议"&#xff09;是一种应用非常广泛的应用层协议&#xff0c;HTTP是基于TCP协议的一种应用层协议。 应用层协议&#xff1a;是计算机网络协议栈中最高层的协议&#xff0c;它定义了运行在不同主机上…...

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

AtCoder 第409​场初级竞赛 A~E题解

A Conflict 【题目链接】 原题链接&#xff1a;A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串&#xff0c;只有在同时为 o 时输出 Yes 并结束程序&#xff0c;否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

UE5 学习系列(三)创建和移动物体

这篇博客是该系列的第三篇&#xff0c;是在之前两篇博客的基础上展开&#xff0c;主要介绍如何在操作界面中创建和拖动物体&#xff0c;这篇博客跟随的视频链接如下&#xff1a; B 站视频&#xff1a;s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

Go 语言接口详解

Go 语言接口详解 核心概念 接口定义 在 Go 语言中&#xff0c;接口是一种抽象类型&#xff0c;它定义了一组方法的集合&#xff1a; // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的&#xff1a; // 矩形结构体…...

基础测试工具使用经验

背景 vtune&#xff0c;perf, nsight system等基础测试工具&#xff0c;都是用过的&#xff0c;但是没有记录&#xff0c;都逐渐忘了。所以写这篇博客总结记录一下&#xff0c;只要以后发现新的用法&#xff0c;就记得来编辑补充一下 perf 比较基础的用法&#xff1a; 先改这…...

【JavaSE】绘图与事件入门学习笔记

-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角&#xff0c;以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向&#xff0c;距离坐标原点x个像素;第二个是y坐标&#xff0c;表示当前位置为垂直方向&#xff0c;距离坐标原点y个像素。 坐标体系-像素 …...

C# 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...