【C/C++】关于 extern “C“ 的理解

详细解释 #ifdef __cplusplus extern "C"

在C++中,#ifdef __cplusplusextern "C" 是用于处理C和C++混合编程中的名称修饰(name mangling)问题的预处理器指令和关键字。

#ifdef __cplusplus

__cplusplus 是一个预处理器宏,在C++编译器中被定义。当你的代码使用这个条件编译指令时,它会检查该宏是否被定义。如果定义了,那么这部分代码将仅在C++编译器中编译。

extern "C"

在C++中,函数和全局变量默认采用名称修饰(name mangling)。这意味着编译器会修改标识符的名字以包含关于参数类型的信息,这有助于避免命名冲突。然而,在C语言中没有这种机制。因此,当你需要从C++调用C代码或反之亦然时,你需要告诉C++编译器不要对这些符号进行名称修饰。

extern "C" 告诉C++编译器应该按照C语言的方式处理接下来的声明。通常你会用它来包围那些需要与C兼容的函数和变量声明。

示例:

假设你有一个C语言的头文件 my_c_lib.h,其中包含以下内容:

 

c

浅色版本

1int add(int x, int y);

你想要在C++项目中使用这个头文件。为了正确地链接到C函数,你需要在C++源文件中这样声明它:

 

cpp

浅色版本

1#ifdef __cplusplus
2extern "C" {
3#endif
4
5int add(int x, int y);
6
7#ifdef __cplusplus
8}
9#endif

在这个例子中,#ifdef __cplusplus 检查宏是否定义,并且只有在C++编译器中才插入 extern "C" 块。如果使用的是纯C编译器,这部分就会被忽略,因为 __cplusplus 在C编译器中未定义。

总结来说:

  • #ifdef __cplusplus 确保 extern "C" 只有在C++环境中才被使用。
  • extern "C" 防止C++编译器对C函数和变量进行名称修饰,确保它们能正确链接到C代码。

上述例子中,如果不采用extern "C" 来修饰函数声明,编译器生成的代码的区别

如果不使用 extern "C" 来声明C函数,C++编译器会对这些函数名进行名称修饰(name mangling),这可能导致链接错误。让我们通过一个具体的例子来说明这一点。

假设你有一个C语言的头文件 my_c_lib.h,其中包含以下内容:

 

c

浅色版本

1// my_c_lib.h
2int add(int x, int y);

以及对应的实现文件 my_c_lib.c

 

c

浅色版本

1// my_c_lib.c
2#include "my_c_lib.h"
3
4int add(int x, int y) {
5    return x + y;
6}

现在,你想在C++项目中使用这个头文件。如果没有使用 extern "C",你可能会这样写:

 

cpp

浅色版本

1// main.cpp (没有使用 extern "C")
2#include "my_c_lib.h"
3
4int main() {
5    int result = add(5, 3);
6    // 使用 add 函数
7    return 0;
8}

不使用 extern "C" 的情况

当编译 main.cpp 时,C++编译器会对 add 函数的名称进行修饰,以包含有关函数签名的信息。例如,如果使用了GCC编译器,修饰后的名称可能是像 _Z3addii 这样的形式,其中 Z 表示函数的结束,3 表示函数名的长度,add 是函数名本身,而 ii 表示两个整数参数。

当你尝试链接 main.cppmy_c_lib.c 时,会发生以下情况:

  • C编译器编译 my_c_lib.c 时,它不会对 add 函数进行修饰,所以链接器会寻找名为 add 的符号。
  • C++编译器编译 main.cpp 时,它会把 add 函数的名称修饰为 _Z3addii,并期望链接器找到相同修饰过的符号。

由于C++编译器和C编译器使用的函数名称不同,链接器无法找到匹配的符号,导致链接失败。

使用 extern "C" 的情况

为了确保C++编译器不进行名称修饰,你可以使用 extern "C" 来声明C函数。在这种情况下,main.cpp 应该这样写:

 

cpp

浅色版本

1// main.cpp (使用 extern "C")
2#ifdef __cplusplus
3extern "C" {
4#endif
5
6#include "my_c_lib.h"
7
8#ifdef __cplusplus
9}
10#endif
11
12int main() {
13    int result = add(5, 3);
14    // 使用 add 函数
15    return 0;
16}

通过使用 extern "C",你告诉C++编译器不要对 add 函数进行名称修饰,从而确保C++编译器和C编译器使用的符号名称是一致的。这样,链接器可以成功找到匹配的符号,并且程序能够正常运行。

总结:

  • 不使用 extern "C":C++编译器会对C函数进行名称修饰,导致链接失败。
  • 使用 extern "C":C++编译器不会对C函数进行名称修饰,保证了与C代码的正确链接。

在提供完整源代码构建的情况下,即 工程包含上述my_c_lib.h my_c_lib.c main.cpp,由于都是采用c++编译器编译,我理解的这种情况下,可以不适用extern "C"

您的理解是正确的。如果整个项目都是使用C++编译器来编译,并且所有的源文件都按照C++标准来处理,那么确实不需要使用 extern "C"。这是因为所有代码都会被C++编译器处理,并且C++编译器会对C函数进行名称修饰(如果需要的话)。在这种情况下,您不会遇到链接问题,因为所有的代码都在同一个命名空间内,并且由同一类型的编译器处理。

然而,在实际开发中,通常会有以下几种情况:

  1. 混合C和C++代码:如果您的项目中既有C文件也有C++文件,或者C库需要在C++环境中使用,那么就需要使用 extern "C" 来确保C++编译器不对C函数进行名称修饰,以便它们能够在链接阶段被正确识别。

  2. 纯C++项目:如果您所有的代码都是C++代码,并且都是用C++编译器编译的,那么您不需要使用 extern "C" 来声明C函数。但是,出于习惯或为了保持一致性,有些人仍然会选择使用它。

对于您提到的例子,如果整个工程都使用C++编译器编译,您可以这样做:

 

cpp

浅色版本

1// my_c_lib.h
2int add(int x, int y);
3
4// my_c_lib.cpp
5#include "my_c_lib.h"
6
7int add(int x, int y) {
8    return x + y;
9}
10
11// main.cpp
12#include "my_c_lib.h"
13
14int main() {
15    int result = add(5, 3);
16    return 0;
17}

在这个情况下,由于所有的文件都使用C++编译器来编译,因此即使不使用 extern "C",编译和链接也应该是没有问题的。C++编译器会为 add 函数生成相同的名称修饰(例如 _Z3addii),并且在链接时能够找到正确的符号。

然而,如果将来您想将此C代码作为库提供给其他项目使用,或者想在其他C++项目中引用这个库,那么最好还是加上 extern "C" 以确保跨项目的兼容性。这是一种很好的编程习惯,可以避免未来可能出现的问题。

在VC环境下,.h头文件中所有函数声明都都在extern "C" 代码块中,而对应函数的定义放在.cpp 源代码中,且.cpp源代码中有使用std::cout 等c++的标准库。这种编程方式我感觉很纠结。

在Visual C++ (VC++) 环境下,如果您的头文件中的函数声明使用了 extern "C",而这些函数在 .cpp 文件中使用了 C++ 特性(如 std::cout),那么这确实会带来一些混淆。但是,这种做法在某些场景下是合理的,尤其是在需要与纯 C 代码交互的情况下。

让我们来看一下具体的例子,以便更好地理解这种情况:

假设您有一个 C 风格的库,但您希望在实现中使用 C++ 的特性,如标准库中的 I/O 流。

头文件 (my_c_lib.h)

 

cpp

浅色版本

1#ifndef MY_C_LIB_H
2#define MY_C_LIB_H
3
4#ifdef __cplusplus
5extern "C" {
6#endif
7
8void printHello();
9
10#ifdef __cplusplus
11}
12#endif
13
14#endif // MY_C_LIB_H

实现文件 (my_c_lib.cpp)

 

cpp

浅色版本

1#include <iostream>
2#include "my_c_lib.h"
3
4void printHello() {
5    std::cout << "Hello from C++!" << std::endl;
6}

主程序 (main.cpp)

 

cpp

浅色版本

1#include "my_c_lib.h"
2#include <iostream>
3
4int main() {
5    printHello();
6    return 0;
7}

这里的关键点在于:

  • 头文件中的 extern "C":这确保了当 C++ 编译器处理该头文件时,它不会对函数名进行名称修饰(name mangling),这样在链接时就能找到对应的函数。

  • .cpp 文件中的 C++ 代码:在实现文件中,您可以自由地使用 C++ 的特性,比如 std::cout

这种结构的好处是:

  1. C 兼容性:通过使用 extern "C",您可以确保其他 C 项目可以链接到您的库,即使它们是在不同的编译器环境下编译的。

  2. C++ 功能:在 .cpp 文件中,您可以充分利用 C++ 的功能,比如类型安全、标准库等。

  3. 封装:头文件隐藏了 .cpp 文件中的实现细节,用户只需要知道函数的接口即可。

  4. 灵活性:这种结构允许您在未来的开发中灵活地扩展库的功能,无论是添加更多的 C 函数还是利用 C++ 的高级特性。

如果您担心这种混搭是否合理,那么通常情况下这是完全可行的。只要您确保 .cpp 文件中的函数签名与头文件中的声明一致,并且整个项目都使用 C++ 编译器来编译,就不会有问题。这种方式在编写跨语言兼容的库时非常常见。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/3280489.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

人工智能大模型发展带来的风险挑战和对策

经过近70年的发展&#xff0c;人工智能技术发展经历了三次起伏&#xff0c;2022年以来&#xff0c;以ChatGPT、Sora等为代表的预训练大模型持续取得突破&#xff0c;推动着人工智能技术从感知向认识&#xff0c;从分析判断式向生成式&#xff0c;从专用向通用进入快速发展的新阶…

PythonDjangoMysql外卖app系统32762-计算机毕业设计项目选题推荐(附源码)

摘 要 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;餐饮外卖当然也不例外。 外卖app系统主要功能模块包括后台首页&#xff0c;轮播图&#xff0c;资源管理&#xff08;餐饮…

【CTFWP】ctfshow-web40

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 题目介绍&#xff1a;题目分析&#xff1a;payload&#xff1a;payload解释&#xff1a;payload2&#xff1a;payload2解释&#xff1a;flag 题目介绍&#xff1a; …

高等数学 第七讲 一元函数积分学的概念和性质_不定积分_定积分_变限积分_反常积分

1.不定积分 文章目录 1.不定积分1.1 原函数1.1.1 原函数与不定积分的定义1.1.2 原函数存在定理 2.定积分2.1 定积分的定义2.2 定积分的精确定义2.3 定积分的几何意义2.4 定积分的存在定理2.5 定积分的性质 3.变限积分3.1 变限积分的定理3.2 变限积分的性质 4.反常积分(待更新) …

红酒标签设计:艺术与品味的结合

在红酒的世界里&#xff0c;每一瓶酒都如同一位优雅的舞者&#xff0c;在酒柜的舞台上静静诉说着自己的故事。而红酒的标签&#xff0c;则是这位舞者身上较华丽的舞裙&#xff0c;它不仅是红酒的身份证明&#xff0c;更是艺术与品味的很好结合。今天&#xff0c;我们就来聊聊红…

重载云台摄像机如何通过国标28181接入到统一视频接入平台(视频国标接入平台)

目录 一、国标GB/T 28181介绍 1、国标GB/T28181 2、内容和特点 二、重载云台摄像机 1、定义 2、结构与设计 3、功能和优势 4、特点 5、应用场景 二、接入准备工作 1、确定网络环境 &#xff08;1&#xff09;公网接入 &#xff08;2&#xff09;专网传输 2、检查重…

STC单片机UART映射printf

文章目录 使用STC-ISP生成UART初始化函数 增加如下函数&#xff0c;注意使用printf函数需要添加 #include <stdio.h> 头文件 #include <stdio.h>void Uart1_Init(void) //9600bps12.000MHz {SCON 0x50; //8位数据,可变波特率AUXR | 0x01; //串口1选择定时器2为…

Vue2从基础到实战(v-bind对于样式控制的增强-操作style,v-model在其他表单元素的使用)

v-bind对于样式控制的增强-操作style 语法&#xff1a;style"样式对象" <div class"box" :style"{ CSS属性名1: CSS属性值, CSS属性名2: CSS属性值 }"></div> 代码解析&#xff1a; HTML结构&#xff1a; 包含了一个div元素&…

OSI七层网络模型:构建网络通信的基石

在计算机网络领域&#xff0c;OSI&#xff08;Open Systems Interconnection&#xff09;七层模型是理解网络通信过程的关键框架。该模型将网络通信过程细分为七个层次&#xff0c;每一层都有其特定的功能和职责&#xff0c;共同协作完成数据从发送端到接收端的传输。接下来&am…

申请美区 Apple ID 完整步骤图解,轻松免费创建账户

苹果手机在下载一些软件时需要我们登录其 Apple ID 才能下载&#xff0c;但是由于一些限制国内的 Apple ID 在 App Store 中有一些限制不能下载某些软件&#xff0c;如何解决这个问题&#xff1f;那就是申请一个美区 Apple ID&#xff0c;怎么申请国外苹果账户呢&#xff1f;下…

WebLogic: CVE-2020-14882/14883【getshell】

记录第一次getshell公网设备 漏洞介绍 CVE-2020-14882&#xff1a;允许 未授权 的用户绕过管理控制台 &#xff08;Console&#xff09;的权限验证访问后台 CVE-2020-14883&#xff1a;允许后台任意用户通过HTTP协议 执行任意命令 使用这两个漏洞组成的利用链&#xff0c;可通过…

Java----代理

什么是代理&#xff1f; 在Java中&#xff0c;代理是一种用于创建一个或多个服务的中间层&#xff0c;它可以拦截并处理程序对实际服务对象的请求。代理模式是一种设计模式&#xff0c;属于结构型模式&#xff0c;它允许程序员在不修改实际对象代码的情况下&#xff0c;增强或控…

【C语言】C语言期末突击/考研--数据的类型

目录 一、编程环境的搭建 二、数据的类型、数据的输入输出 2.1.数据类型 2.2.常量 2.3.变量 2.4.整型数据 2.4.1.符号常量 2.4.2.整型变量 2.5.浮点型数据 2.5.1.浮点型常量 2.5.2.浮点型变量 2.6.字符型数据 2.6.1字符型常量 2.6.2.字符数据在内存中的存储形式及…

解决jenkins配置extendreport不展示样式

下载插件&#xff1a;Groovy 、 HTML Publisher plugin 配置&#xff1a; 1&#xff09;Post Steps &#xff1a; 增加 Execute system Groovy script &#xff0c; 内容&#xff1a; System.setProperty("hudson.model.DirectoryBrowserSupport.CSP", "&qu…

MyBatis基础配置

一、M y B a t i s 配 置 文 件 1.为什么学习MyBatis配置文件 功能&#xff1a;构建SqlSessionFactory的依据。 意义&#xff1a;MyBatis最为核心的内容&#xff0c;对MyBatis的使用影响很大。 注意&#xff1a;配置文件的层次顺序不能颠倒&#xff0c;一旦颠倒会出现异常。 …

清空了最近删除的照片可以找回吗 照片删除了 最近删除里面也清空了 怎么恢复

我们会把喜欢的照片保存在电脑中&#xff0c;并经常会删除一些不再需要的照片。由于电脑中的照片太多&#xff0c;我们可能会出现一些失误操作。我们每次删除照片之后都要及时查看电脑中的照片&#xff0c;如果发现有失误操作导致照片被删除&#xff0c;可以立即采取措施恢复照…

一款以流程管理为核心的信息化快速开发的办公OA平台,一体化工作平台,开箱即用,私活利器(附源码)

前言 在当今的企业运营中&#xff0c;信息孤岛和系统分散问题日益凸显。员工常常需要在多个系统间切换&#xff0c;以完成不同的工作任务&#xff0c;这不仅效率低下&#xff0c;还容易出错。 为了处理这一痛点&#xff0c;一种集成化的办公平台应运而生&#xff0c;它能够将各…

项目分享:用友yonsuite对接吉客云

项目背景&#xff1a; 客户背景&#xff1a;广州市某电子科技有限公司自1997年创立游戏手柄品牌&#xff0c;专注于游戏外设领域&#xff0c;是国内游戏手柄行业的领军企业。业务规模包括线上平台50多家&#xff0c;线下客户600多家&#xff0c;日单量10000多单。所属行业为电子…

Undefined symbol xTaskGetCurrentTaskHandle (referred from stream_buffer.o).

Undefined symbol xTaskGetCurrentTaskHandle (referred from stream_buffer.o). 解决办法