extern “C“ 的作用、C++ 和 C 编译的不同、C++ 编译过程的五个主要阶段

news2024/11/25 12:50:29

在 C++ 中,如果需要从 C 语言导入函数或与 C 代码交互,需要使用 extern "C" 关键字。这是因为 C++ 和 C 在编译过程中的 符号命名机制(即 "名称修饰" 或 "name mangling")不同。

1. extern "C" 的作用

extern "C" 关键字的主要作用是告诉 C++ 编译器,所包含的函数或变量使用的是 C 语言的命名和链接规则,而不是 C++ 的。这对于编译和链接 C++ 代码与 C 代码特别重要,因为 C++ 编译器在编译函数时会进行名称修饰,而 C 编译器不会。

C++ 名称修饰:C++ 支持函数重载、类成员函数等复杂的功能,因此 C++ 编译器会对函数名进行编码,以区分同名的不同函数。这个过程称为 名称修饰(name mangling)。

而 C 语言不支持函数重载,其编译器不会对函数名进行修饰,保持函数名的简单性。因此,为了在 C++ 中使用 C 编写的函数,必须使用 extern "C" 来告知 C++ 编译器使用 C 语言的链接规则。

2. extern "C" 的用法

导入单个 C 函数

extern "C" {
    void my_c_function(int a);
}

这段代码告诉 C++ 编译器,my_c_function 函数是用 C 语言实现的,并且在编译时应遵循 C 语言的符号命名规则。

导入多个 C 函数

可以将多个函数的声明包含在 extern "C" 块中:

extern "C" {
    void my_c_function1(int a);
    int my_c_function2(double b);
}

导入 C 头文件

如果你想在 C++ 中包含一个由 C 编写的头文件,可以使用 extern "C" 包装整个头文件的内容:

extern "C" {
    #include "my_c_header.h"
}

或者,头文件本身可以被修改,使用条件编译保证其既可以在 C++ 中使用,也可以在 C 中使用:

#ifdef __cplusplus
extern "C" {
#endif

// C 函数声明
void my_c_function(int a);

#ifdef __cplusplus
}
#endif

在这种情况下,当头文件在 C++ 中被包含时,extern "C" 生效,而在 C 中编译时则不受影响。

3. C++ 和 C 编译的不同

3.1. 名称修饰(Name Mangling)

如前所述,C++ 编译器会对函数名进行名称修饰,以支持函数重载、命名空间和类成员函数等特性。名称修饰使得同一个函数名可以根据其参数、命名空间等特征进行区分。C 编译器则不做名称修饰,所有的函数名在编译后保持原样。

C++ 名称修饰示例

在 C++ 中,编译器可能会将函数 int my_function(int) 的名字修饰成 _Z11my_functioni,其中包括函数名和参数类型的编码。

C 函数名示例

在 C 语言中,函数 int my_function(int) 仍然保持为简单的 my_function

名称修饰的影响:
  • C++ 中的函数重载:C++ 支持函数重载,因此会为每个重载的函数生成不同的修饰名称。
  • C 函数命名:由于 C 不支持函数重载,所有函数的名称在编译时保持唯一,编译器不需要进行名称修饰。
3.2. 链接规则
  • C 链接:C 编译器在链接时只需要处理简单的函数名称。
  • C++ 链接:C++ 编译器在链接时需要处理名称修饰后的符号,因而 C++ 函数可以根据参数、类、命名空间等进行链接。
3.3. 语言特性
  • C++ 编译器:处理复杂的 C++ 语言特性,如类、模板、命名空间、异常处理、虚函数等。C++ 代码在编译时往往比 C 代码复杂得多。
  • C 编译器:处理 C 语言的相对简单的语法和功能,不需要考虑面向对象的特性。

4. extern "C" 的使用场景

  • C++ 调用 C 函数:当你在 C++ 代码中需要使用 C 库时,必须通过 extern "C" 来确保 C++ 编译器按照 C 的方式处理函数的链接。典型场景包括使用 C 编写的系统库、第三方库(如 POSIX、OpenSSL 等)。
  • C++ 提供 C 接口:如果你编写了 C++ 库,且需要提供给 C 语言使用,那么可以通过 extern "C" 来导出 C 风格的接口,使 C 语言能够调用 C++ 编译的库。
示例:C++ 调用 C 函数

假设有一个 C 编写的函数 my_c_function

// my_c_code.c
#include <stdio.h>

void my_c_function(int a) {
    printf("Value: %d\n", a);
}

在 C++ 中调用这个 C 函数时,需要使用 extern "C"

// my_cpp_code.cpp
extern "C" {
    void my_c_function(int a);
}

int main() {
    my_c_function(10);  // 调用C函数
    return 0;
}

示例:C++ 提供 C 接口

// my_cpp_code.cpp
extern "C" {
    void my_cpp_function(int a);
}

void my_cpp_function(int a) {
    // C++ 实现
    std::cout << "C++ Function: " << a << std::endl;
}

这个函数可以被 C 代码调用,因为它的符号遵循 C 的规则。

总结

  1. extern "C":用于告诉 C++ 编译器按照 C 的方式处理函数的链接,使 C++ 和 C 代码能够互相调用。
  2. C++ 编译和 C 编译的不同
    • 名称修饰:C++ 编译器会进行名称修饰,以支持函数重载和其他高级功能,而 C 编译器不会。
    • 链接规则:C++ 链接时处理更复杂的符号,而 C 语言保持简单的函数名。
  3. 常见使用场景
    • C++ 调用 C 库时需要 extern "C"
    • 提供 C++ 库接口给 C 语言使用时,也需要使用 extern "C"

C++ 程序从编写代码到生成可执行的二进制文件,通常经历以下 五个步骤,即:编写代码预处理编译汇编链接。每个步骤都会转换或处理代码,最终生成一个可执行文件。

C++ 编译过程的五个主要阶段

  1. 编写代码(Source Code Writing)
  2. 预处理(Preprocessing)
  3. 编译(Compilation)
  4. 汇编(Assembly)
  5. 链接(Linking)

1. 编写代码(Source Code Writing)

这是程序员编写的 C++ 源代码,通常使用文件扩展名 .cpp.h。C++ 源文件可能包含类、函数、数据结构和其他逻辑。

  • 源文件:例如 main.cpp, MyClass.cpp, MyClass.h

2. 预处理(Preprocessing)

预处理是编译的第一个阶段,C++ 编译器中的 预处理器 会处理所有以 # 开头的指令,如 #include, #define 等。这一步的主要任务是处理宏替换、文件包含和条件编译指令。

主要任务

  • 文件包含:将 #include 的头文件内容插入源文件。
  • 宏替换:将所有宏定义(通过 #define 定义的内容)替换为对应的值。
  • 条件编译:根据条件指令如 #ifdef#endif 等执行代码的保留或忽略。
  • 注释删除:删除所有的注释内容,包括单行注释 // 和多行注释 /* ... */

预处理后的文件仍然是文本文件,通常称为 扩展文件,但它包含了所有展开的宏、包含的头文件等。

预处理示例

#include <iostream>
#define MAX 100

int main() {
    std::cout << MAX << std::endl;
    return 0;
}

预处理后变成:

// 假设 <iostream> 已被展开
// 省略头文件展开的内容...

int main() {
    std::cout << 100 << std::endl;  // 宏 MAX 被替换为 100
    return 0;
}

3. 编译(Compilation)

在这一步,编译器 将预处理后的源代码文件(仍然是人类可读的 C++ 代码)转换为 中间代码,通常称为 汇编代码。汇编代码是面向特定硬件架构的一种低级别语言,但仍然是人类可读的符号代码。

编译的过程

  • 语法分析:编译器对源代码进行语法分析,确保代码符合 C++ 的语法规则。
  • 语义分析:检查代码的逻辑是否合理,比如类型检查、变量是否已声明等。
  • 生成汇编代码:编译器根据目标架构将 C++ 代码转换为汇编语言。

编译后的文件:通常以 .s 为扩展名,它仍然是人类可读的汇编代码。

编译示例

int main() {
    int a = 10;
    return a;
}

编译后的汇编代码(示例):

    .section    __TEXT,__text,regular,pure_instructions
    .globl  _main
_main:                                  ## @main
    .cfi_startproc
## %bb.0:
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    movl    $10, %eax
    popq    %rbp
    retq
    .cfi_endproc

4. 汇编(Assembly)

汇编器(Assembler)将编译生成的汇编代码转换为机器码(Machine Code),也称为 目标文件(Object File)。目标文件是特定于目标机器的二进制文件,但还不是完整的可执行文件,因为它可能仍然有外部依赖(比如其他模块中的函数)。

汇编的过程

  • 将汇编代码转换为机器指令,这些指令是计算机 CPU 可以直接理解并执行的二进制代码。

目标文件

  • 汇编器生成的目标文件通常具有 .o(Unix/Linux/Mac 系统)或 .obj(Windows 系统)扩展名。
  • 每个 C++ 源文件都会生成一个对应的目标文件。

示例: 一个目标文件内部包含计算机能执行的机器代码(无法直接展示二进制内容)。

5. 链接(Linking)

链接器(Linker)将多个目标文件和库文件组合起来,生成最终的可执行文件。链接器的主要任务是解决符号引用,即将函数调用和全局变量的引用链接到其实际定义上。

链接的过程

  • 符号解析:链接器会将各个目标文件中的未解析的符号(如函数、全局变量等)与其他目标文件或库中定义的符号进行匹配。
  • 库链接:如果程序使用了外部库(如标准库、动态库或静态库),链接器会将这些库与目标文件链接起来。
  • 生成可执行文件:链接器最终生成一个完整的二进制可执行文件,该文件可以直接在目标平台上运行。

链接示例

g++ main.o MyClass.o -o my_program

这条命令会将 main.oMyClass.o 链接在一起,生成可执行文件 my_program

6. 可执行文件(Executable File)

生成的可执行文件是二进制文件,包含了最终的机器代码以及所有的依赖库和符号。这个文件可以在目标系统上直接运行。

  • LinuxMac 系统的可执行文件没有特定的扩展名,通常通过运行 ./my_program 执行。
  • Windows 系统上的可执行文件通常有 .exe 扩展名,可以通过双击或命令行运行。

可视化编译过程

编译过程中的工具

  • 预处理器:负责处理 # 开头的预处理指令(如 #include, #define)。
  • 编译器:将 C++ 代码翻译为汇编代码(如 g++clang)。
  • 汇编器:将汇编代码转为机器码,生成目标文件(如 as)。
  • 链接器:负责将目标文件链接成可执行文件(如 ld)。

编译选项

在编译时可以使用一些编译器选项来控制不同的阶段。例如,使用 GCC 时:

  • -E:只执行预处理,不编译。
  • -S:将 C++ 代码编译成汇编代码,生成 .s 文件。
  • -c:只编译,不链接,生成目标文件 .o
  • -o:指定生成的可执行文件的名称。

总结

C++ 程序从代码到可执行文件的过程是通过预处理、编译、汇编和链接这几个步骤完成的。每个阶段都有特定的任务,最终生成可以在目标平台上运行的二进制文件。

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

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

相关文章

怎么把一段音频的人声和背景音乐分开?

在数字音频处理中&#xff0c;将一段音频中的人声和背景音乐分开是一个复杂但又常见的需求。这种技术广泛应用于音乐制作、影视后期、广告制作等多个领域。本文将为你详细解析如何通过不同的方法实现这一目标&#xff0c;帮助你更好地掌握音频分离技术。 一、音频分离的基本概念…

无人直播自动化回复客户咨询

我们插件是根据页面元素变动进行自动化操作的&#xff0c;想要实现网页版自动化&#xff0c;必须了解html以及dom结构&#xff0c;还有xpath定位方法。 各大直播后台页面结构不一样&#xff0c;所以要进行兼容处理&#xff0c;我们一个插件支持以下直播或客服平台 唯一客服浏…

数据结构与算法:栈与队列的高级应用

目录 3.1 栈的高级用法 3.2 队列的深度应用 3.3 栈与队列的综合应用 总结 数据结构与算法&#xff1a;栈与队列的高级应用 栈和队列是两种重要的线性数据结构&#xff0c;它们在计算机科学和工程的许多领域都有广泛的应用。从函数调用到表达式求值&#xff0c;再到任务调度…

c#的opcua客户端源码,支持用户名密码

在工作中遇到需要采集西门子机床的opcua和kepServer的opcua&#xff0c;找了一些网上的demo都不能都连接成功&#xff0c;好不容易找到一个两个都支持的c#opcua客户端源码&#xff0c;分享给大家。 软件界面如下 连接kepserver成功的界面 连接西门子opcua的界面 c#源码链接如下…

开关电源调制模式和工作模式

开关电源调制模式和工作模式 ‌‌开关电源定义开关电源分类‌单管DC/DC和、双管DC/DC和四管DC/DC的主要区别正激和反激 DCDCBUCK原理BOOST原理BUCK-BOOST原理异步整流和异步整流同步整流异步整流同步和异步整流区别同步和异步整流优缺点 DCDC调制模式PWM&#xff08;Pulse Widt…

C++ | Leetcode C++题解之第475题供暖器

题目&#xff1a; 题解&#xff1a; class Solution { public:int findRadius(vector<int>& houses, vector<int>& heaters) {sort(houses.begin(), houses.end());sort(heaters.begin(), heaters.end());int ans 0;for (int i 0, j 0; i < houses.…

轻松掌握TCP与UDP核心机制

White graces&#xff1a;个人主页 &#x1f649;专栏推荐:Java入门知识&#x1f649; &#x1f649; 内容推荐:网络编程TCP&#x1f649; &#x1f439;今日诗词:Best wishes&#x1f439; ⛳️点赞 ☀️收藏⭐️关注&#x1f4ac;卑微小博主&#x1f64f; ⛳️点赞 ☀️收…

相机曝光的两种模式

相机的曝光模式 相机帧率和曝光时间的关系 相机的图像采集包括曝光(Exposure)和读出(Readout)两部分 曝光又分为非重叠(non-overlapped)曝光和重叠(overlapped)曝光两种 在非重叠(“non-overlapped”)模式中&#xff0c;每个图像采集的周期中&#xff0c;相机在下一个图像采集…

萝卜快跑:迈向全球,挑战与机遇并存

萝卜快跑&#xff1a;迈向全球&#xff0c;挑战与机遇并存 引言 萝卜快跑的崛起 萝卜快跑的优势 萝卜快跑面临的挑战 未来展望 引言 在当今科技飞速发展的时代&#xff0c;自动驾驶技术成为了全球瞩目的焦点。众多科技巨头纷纷投入大量资源&#xff0c;试图在这个新兴领域…

文件完整性监控:如何提高企业的数据安全性

企业网络庞大而复杂&#xff0c;需要处理大量关键业务数据&#xff0c;这些敏感文件在企业网络中不断传输&#xff0c;并由多个用户和实体存储、共享和访问。FIM 工具或具有 FIM 功能的 SIEM 解决方案使企业能够跟踪未经授权的文件更改、对敏感信息的恶意访问、数据篡改尝试和内…

FreeRTOS——空闲任务和钩子函数介绍

空闲任务 在前面的学习中我们提到&#xff0c;空闲任务会负责释放一些被删除任务的内存&#xff0c;在FreeRTOS中&#xff0c;用户分配的内存通常也是在空闲任务中释放的。空闲任务是一个特殊的任务&#xff0c;当没有其他任务需要运行时&#xff0c;系统将会调度空闲任务来执行…

硬盘格式化后能恢复数据吗?好用4款工具集锦

嘿&#xff0c;硬盘格式化后能恢复数据吗&#xff1f;咱们现在的生活&#xff0c;数据可是宝贝&#xff0c;这大家都清楚。学习用的资料、工作的文件&#xff0c;还有那些宝贵的照片、视频&#xff0c;统统都存硬盘里。万一硬盘不小心被格式化了&#xff0c;那感觉就像所有东西…

【GUI】使用 PySide6 开发图片左右切换软件

使用 PySide6 开发图片左右切换软件 前言 在现代软件开发中&#xff0c;使用 Python 开发跨平台的 GUI 应用程序变得非常普遍。今天&#xff0c;我们将使用 PySide6 来开发一个简单的图片浏览器&#xff0c;它可以实现图片左右切换的功能&#xff0c;并自适应按钮布局。本教程…

闭着眼学机器学习——朴素贝叶斯分类

引言&#xff1a; 在正文开始之前&#xff0c;首先给大家介绍一个不错的人工智能学习教程&#xff1a;https://www.captainbed.cn/bbs。其中包含了机器学习、深度学习、强化学习等系列教程&#xff0c;感兴趣的读者可以自行查阅。 1. 算法介绍 朴素贝叶斯是一种基于贝叶斯定理…

c++应用网络编程之十一Linux下的epoll模式基础

一、epoll模式 在前面分析了select和poll两种IO多路复用的模式&#xff0c;但总体给人的感觉有一种力不从心的感觉。尤其是刚刚接触底层网络开发的程序员&#xff0c;被很多双十一千万并发&#xff0c;游戏百万并发等等已经给唬的一楞一楞的。一听说只支持一两千个并发&#x…

YOLOv9分割改进 ,YOLOv9分割改进主干网络为华为EfficientNet,助力涨点

YOLOv9 分割改进前训练结果: YOLOv9 分割改进后训练结果: 摘要 卷积神经网络(ConvNets)通常在固定的资源预算下开发,然后在有更多资源时进行扩展以提高准确性。在本文中,我们系统地研究了模型扩展,并发现仔细平衡网络深度、宽度和分辨率可以带来更好的性能。基于这一…

【Python库安装】Python环境安装wrf-python库

【Python库安装】Python环境安装wrf-python库 wrf-python库概述Python中安装wrf-python库方式1&#xff1a;使用pip安装方式2&#xff1a;离线安装&#xff0c;使用whl文件安装另&#xff1a;报错 方式3&#xff1a;使用conda安装wrf-python另&#xff1a;报错 参考 wrf-python…

教育培训系统小程序的设计

教师账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;课后习题测试管理&#xff0c;观看进度管理&#xff0c;论坛管理&#xff0c;网课信息管理&#xff0c;公告信息管理&#xff0c;学生管理&#xff0c;试卷管理&#xff0c;测试管理 微信端账号功能包括…

RK3568学习之Nginx移植+RTMP推流

1.下载 Nginx 源码 进入到 Ubuntu 系统的某个目录下&#xff0c;下载 Nginx 源码&#xff1a; wget http://nginx.org/download/nginx-1.20.0.tar.gz这里我们下载的是 1.20 版本&#xff0c;这是比较新的版本了。下载完成之后将得到一个名为 nginx-1.20.0.tar.gz的压缩包文件…

嵌入式中数据库sqlit3基本使用方法与现象

大家好,今天主要给大家分享一下,数据库的使用方法,观察对应的效果。 第一:数据库sqlit3基本安装方法 sqlite3 安装 使用 Ubuntu # 安装软件 sudo apt-get install sqlite3# 查看版本 sqlite3 -version# 安装编译工具包 sudo apt-get install sqlite3-dev# 安装可视化工具…