【C++】编译

三、C++编译

前面给大家演示了如何从写C++代码到编译代码再到执行代码的全过程。这个过程中非常重要的编译环节,被我们一个按钮或者一个ctrl+F7快捷键就给带过了。其实这个环节非常重要,如果你非常了解这个环节,你开发源代码就会更加自信和清醒,而不是迷迷糊糊,摸棱两可……

下图直观描述了从源代码到计算机执行完毕的各个大环节:

内容实在多,图上没位置了,这里就继续说明上图。上图的1234是几个大步骤。1是我们的开发环境,你的代码就是在这个平台上书写、调试、编译、执行。。。等等

上图2是我们在1中开发的代码。这4行代码的功能就是把一个数字3存储到内存。但是A是我们人类能看懂的英文文字、数字3、一些符号,比如分号、小括号、花括号等等。那cpu可是看不懂这些呀,所以我们要把A编译成D2,因为cpu可以看懂D2,cpu就可以执行了D2了,执行完毕就可以完成我们人类的任务:写个3到内存。本部分重点讲3这个过程,就是如何编译的。

那我们再讲点题外的东西,以便我们对整体流程有一个认识。那就是后面的4,4说的就是cpu是如何执行D2的。我们说了D2是可执行二进制指令,也就是机器码(machine code),这些机器码先按照字节为单位加载到内存,就是E,当然E会很长很长,上图我就画了2个内存单位。加载完毕后就逐个单元地送到CPU。CPU只是一些电路(当然这个电路会及其复杂,cpu又称芯片,要不怎么芯片被卡脖子呢),它功能其实非常简单,它只会从内存读取数据、往内存中写入数据。当内存的这些指令数据送到CPU后,有用这些指令0101,就是高电平低电平的意思,那这些不同的特殊组合的高低电平流就会引起cpu中的晶体管打开或关闭,也就是引起不同的电路,不同的电路又会产生不同的输出结果,不同的输出结果指的是不同的高低电平的输出,也就是有序的0101序列的输出,而输出就是输出到内存。而内存上不同序列的0101又和人类认识的符号相对应。于是输出就又能映射成人类理解的东西,比如屏幕上的一行字,比如声卡输出的一段音频等等人类的东西。
具体到我们上图的例子,cpu输出的结果就是在内存的某个内存单元里面存了个3。那cpu具体是如何执行F的呢?我们看4.1,4.1中的H就是部分机器码,都是0101,我们看不懂,没关系,我先把这0101转化为16进制,就是J,J我们还看不懂,那我们把J在转化成K(这个过程叫优化,比如全部是0的就是空行的意思,也就是啥也不用做,所以可以删除了。关于优化环节我们后面还要单独拿出来演示),K还是看不懂,那我们把K再转化为L和M。L和M我们人类认识吧,这就是汇编语言,至少里面mov是move的意思,是我们人类的tocken,呵呵。。其中L的意思启动和结束的意思,仅仅是M这行代码是我们人类的任务:mov一个数字3到内存的后面表示的那个内存地址里面。这才是我们人类任务执行完毕的整个流程。

看似已经洋洋洒洒写了很多了,其实这个流程也是只是个大概。上图中每个箭头都表示从这一步到下一步的意思,其实每步还有很多很多的细节。本小节,我们只把步骤3——编译,拿出来展开讲解。

1、明确几个概念

我们的文本代码.cpp需要转化成.exe可执行程序,cpu才可以执行。
从.cpp到.exe的过程,我们就笼统的叫做编译,其实这个过程是需要经过下面3个子过程的:

(1)预处理(Preprocess):从.cpp变成.i文件。是处理一些预处理语句。

(2)编译(compile):从.i文件到.obj文件。这一步是编译的核心。这一步还要分很多个子环节,也是本部分的重点。其实如果你源文件没有引用其他文件或库之类的,就像上图的示例中的源代码,啥也没有,那其实到这步结束,生成.obj文件就已经是二进制可执行文件了,cpu就可以执行了。但事实上,我们不会写上例中那么无聊的代码,至少我们要写一个在屏幕上打印个hello world啥的。那在屏幕上打印东西都已经不是那么简单了,此时你写的源代码就需要引入头文件啥的,那此时生成的.obj文件就没法放cpu上执行,因为.obj文件里面有引用其他文件的代码,而且其他文件代码cpu也不知道,所以就没法执行。所以此时你还得进行链接link。尤其是我们在实际项目开发过程中,我们不仅会有很多引用,我们还会有很多.cpp文件。compile只是对每个.cpp文件分别进行编译,分别生成每个文件的.obj文件。那此时就非常有必要再有个链接器把我们这些所有的.obj文件都链接起来。

(3)链接(Link):从.obj文件到.exe文件。为什么要链接,其实上面已经说得非常清楚了。一般情况下我们的源代码编译都是要进行链接的,链接完毕后就是.exe文件,cpu就可以执行了。

所以,平时我们经常说的编译其实是预处理+编译+链接,其实就是build,build=preprocess+compile+link。如果你的源代码简单到无聊,那你可能就不需要link了,但一定得预处理和编译才能执行。

  • 此外,这里还得梳理几个概念:

(1)编译器是把我们每个.cpp文件都分别编译成独立的.obj文件,所以在这个过程中每个.cpp文件都是一个翻译单元(translation unit),一个翻译单元生成一个obj文件。

在C++中,文件翻译单元是指一个.cpp文件以及它包含的所有头文件组合到一起形成的单独编译单元。每个.cpp文件在编译时都会生成一个翻译单元。例如,假设你有一个名为main.cpp的源文件和一个名为utils.h的头文件。main.cpp包含#include "utils.h"。在编译main.cpp时,utils.h中的所有内容也会被插入到main.cpp中,形成一个翻译单元,然后这个翻译单元会被编译成可执行文件或对象或库代码。
在这个例子中,main.cpp和utils.h组合在一起形成了一个翻译单元,utils.cpp是另一个翻译单元。在编译时,每个.cpp文件都会独立编译成一个对象文件或库或可执行程序。最后,链接器会将这些对象文件和库文件和可执行文件合并成最终的一个可执行文件。

(2)C++是不关心文件名的,不像Java语法,你的类名和你的文件名得一样、你得文件夹层次要和你的package一样。但是C++不一样:在于C++中,文件只是提供给编译器源代码的一种方式。文件名只负责告诉编译器你给它的是什么类型的文件、以及这种类型文件编译器应该如何处理它。比如你给编译器一个.cpp的文件,编译器就把这个文件当作C++文件进行处理;当你给编译器一个.C或者.H文件,编译器会把.C文件当成C语言文件进行处理,而不是当作C++文件进行处理,同时把.H文件当作头文件进行处理。这些都是默认的约定。当然你也可以更改,比如你给编译器一个.abcdef文件,那你就得再告诉编译器这种文件你按照C++文件进行处理,也是可以的。

(3)文件是指一组相关数据的有序集合,这个数据集的名称叫做文件名。例如源程序文件(.cpp)、目标文件(.obj)、可执行文件(.exe)、库文件(头文件)等。文件通常是驻留在外部介质(如磁盘等)上的,在使用时才调入内存中来,这就是为什么对文件操作时需要打开和关闭的原因。
在C++语言中,文件是一个流的概念。头文件fstream定义了三个类型:
ifstream:从一个给定文件读取数据;
ofstream:向一个给定文件写入数据;
fstream:读写给定文件。
类型的操作和cin、cout一样,可以用IO操作符(<<和>>)来读写文件,也可以用getline函数读取一整行数据。

(4)在 C++ 程序中,符号(例如变量或函数名称)可以在其范围内进行任意次数的声明。 但是,一个符号只能被定义一次。 这就是“单一定义规则”(ODR)。

这些概念是我们理解后面的基础,目前我能想到的就是这么多。下面我们就展开编译的3个子过程,详细聊:

2、预处理(Preprocess):
编译器中进行预处理操作(Preprocess)的是预处理器(Preprocessor), 也是第一个上场的程序。预处理器(Preprocessor)是对C++程序源代码进行简单替换和增删的一个操作。
预处理不对程序的源代码进行解析,仅仅是替换或删除某些代码块而已。所以预处理完毕后文件(.i文件)还是一个字符串文件,就是还是都是英文单词和数字那样的字符文件。

那么预处理是如何进行增删替换的呢?当然就是人为给它定义一些规则,符号规则就增删替换。
那也所以,预处理的工作流程就是:逐行遍历.cpp文件中的所有代码行,遇到预处理语句时,根据规则增删。
那么哪些是预处理语句?哪些是要替换的?用谁替换?哪些又是删除的?

上面是预处理原理,下面我们看看预处理的实操过程:

从上图可以看出我们的.cpp文件只有1k,但是build完毕后竟然有几十k,原因就是编译器进行了预处理。

我们对比一下有预处理语句的cpp文件和无预处理语句的cpp文件,编译后的差别:

那么预处理到底是不是简单的替换和删除呢?看下面例子:


为啥Math.cpp编译后没大多少啊?因为仅仅是从EndBrace.h文件中复制了一个后花括号而已。就是我前面说的,预处理仅仅是复制粘贴一些文本而已。这里就是把.h文件中的字符}复制到Math.cpp中的include那行位置上而已,或者说就是替换了include那行而已。那到底是不是你说的啊?我们看看生成的预处理文件.i文件就能证明了:


可见是不是就是仅仅是用头文件中的}替换了源文件中include那行代码,就是一个删除并替换的动作。下面我们再看看其他情况下的.i文件是不是也是按照前面说的规则生成的:

可见,在预处理过程中主要就是,处理源代码中的预处理语句:引入头文件(替换)、去除注释(删除)、处理所有的条件编译指令(该删除的删除,不该的留下),宏的替换(替换),添加行号(添加),保留所有的编译器指令(添加)。

以上就是预处理,下面我们看更核心部分:

3、编译(Compile)

待续。。。。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CorelDRAW 2024开启设计新纪元,终身永久版与中文破解版的全面解析及安装攻略

当我们谈论图形设计软件时&#xff0c;CorelDRAW无疑是一个响亮的名字。作为一款强大的矢量图形编辑工具&#xff0c;它以其丰富的功能和用户友好的界面赢得了全球设计师的喜爱。随着CorelDRAW 2024的发布&#xff0c;这个备受瞩目的版本带来了前所未有的创新特性&#xff0c;进…

Vulnhub-DC-1,7

靶机IP:192.168.20.141 kaliIP:192.168.20.128 网络有问题的可以看下搭建Vulnhub靶机网络问题(获取不到IP) 前言 1和7都是Drupal的网站&#xff0c;只写了7&#xff0c;包含1的知识点 信息收集 用nmap扫描端口及版本号 进入主页查看作者给的提示&#xff0c;不是暴力破解的…

ROS2底层机制源码分析

init ->init_and_remove_ros_arguments ->init ->Context::init 保存初始化传入的信号 ->install_signal_handlers→SignalHandler::install 开线程响应信号 ->_remove_ros_arguments 移除ros参数 ->SingleNodeManager::instance().…

条件判断if语句与case语句

一、条件测试 test命令进行条件测试&#xff0c;然后根据返回值来判断条件是否成立。 常用操作符&#xff1a; -e &#xff1a;既可以测试文件又可以测试目录是否存在 -d &#xff1a;测试目录是否存在 -f &#xff1a;测试文件是否存在 -r &#xff1a;测试当前用户是否…

前端加载 动画特效

效果图: 完整代码: <!DOCTYPE html> <html> <head><meta charset="UTF-8" /><title>加载动画</title><style type="text/css">/* 设置页面背景颜色 */body {background: #ECF0F1;}/* 定义加载动画容器的样式…

抖店没人做了?不是项目不行了,而是商家们都换思路去玩了

我是王路飞。 有没有发现现在很多抖店新手都在吐槽&#xff0c;抖店不好做了&#xff0c;做不起来&#xff0c;没人做了&#xff0c;太内卷了...... 对这种做不起来还在怪项目本身的&#xff0c;一定要离他远一点&#xff0c;省得被他的负能量给影响到自己的状态。 任何项目…

怎么学习汇川Codesys PLC教程?

前言 各位好&#xff0c;我在B站和抖音上都有发布视频的&#xff0c;搜索我的名称“阿凡工控分享”即可。在CSDN上发表文章也是想把我的一点见解和经验分享出来&#xff0c;进一步的方便大家进行学习。 我是正文 本文主要也是为了方便大家学习汇川的Codesys PLC而制作的&…

Java并发编程之线程池源码解析与实现详解

Java并发编程是现代开发中非常重要的一个领域。使用线程池来管理和控制线程的创建和生命周期&#xff0c;可以更加高效地利用系统资源&#xff0c;提高系统的并发性能。线程池是一种常见的并发编程模型&#xff0c;Java提供了ThreadPoolExecutor类来实现线程池。本文将详细解析…

⌈ 传知代码 ⌋ MonoCon解读与复现

&#x1f49b;前情提要&#x1f49b; 本文是传知代码平台中的相关前沿知识与技术的分享~ 接下来我们即将进入一个全新的空间&#xff0c;对技术有一个全新的视角~ 本文所涉及所有资源均在传知代码平台可获取 以下的内容一定会让你对AI 赋能时代有一个颠覆性的认识哦&#x…

写给大数据开发:好的指标定义、特性与业务价值

在大数据时代&#xff0c;数据的质量对于业务决策和数据分析至关重要。好的数据不仅丰富&#xff0c;而且准确、及时&#xff0c;为业务提供有力支撑。 文章目录 数据定义与特性数据对业务的价值指标展示与规范化定义一些指标的定义好的数据是业务成功的关键 数据定义与特性 好…

在 Wed 中应用 MyBatis(同时使用MVC架构模式,以及ThreadLocal 事务控制)

1. 在 Wed 中应用 MyBatis&#xff08;同时使用MVC架构模式&#xff0c;以及ThreadLocal 事务控制&#xff09; 文章目录 1. 在 Wed 中应用 MyBatis&#xff08;同时使用MVC架构模式&#xff0c;以及ThreadLocal 事务控制&#xff09;2. 实现步骤&#xff1a;1. 第一步&#xf…

python爬虫爬取微博评论--完整版(超详细,大学生不骗大学生)

目录 一、找到页面 二、学会使用检查元素 2.1 打开检查元素界面 2.2 找到所有评论所在的位置 2.2.1 搜索评论 2.2.2 找到data表 三、基础部分代码实现 四、格式化输出 4.1 了解存储格式 4.2 单独取出内容 4.3 取出所有评论内容 4.4 格式化读取信息 五、导出成表格…

【ARM Cache 与 MMU 系列文章 7.4 -- ARMv8 MMU 配置 寄存器使用介绍】

请阅读【ARM Cache 及 MMU/MPU 系列文章专栏导读】 及【嵌入式开发学习必备专栏】 文章目录 MMU 转换控制寄存器 TCR_ELxTCR_ELx 概览TCR_ELx 寄存器字段详解TCR 使用示例Normal MemoryCacheableShareability MMU 内存属性寄存器 MAIR_ELxMAIR_ELx 寄存器结构内存属性字段Devic…

两种典型的嵌入式系统架构模式

大多数嵌入式系统都具备实时特征&#xff0c;那么&#xff0c;这种嵌入式系统的典型架构可概括为两种模式&#xff0c;即层次化模式架构和递归模式架构。 1.层次化模式架构 为了达到概念一致性&#xff0c;许多系统通过层次化的方法进行搭建。这样做的结果是&#xff1a;位于高…

【漏洞复现】海洋CMS /js/player/dmplayer/dmku/ SQL注入漏洞复现(CVE-2024-29275)

0x01 产品简介 海洋CMS是一套专为不同需求的站长而设计的内容管理系统&#xff0c;灵活、方便、人性化设计、内容的专业网站。海洋CMS基于PHPMySql技术开发&#xff0c;完全开源免费、无任何加密代码。简单易用是最大的特色&#xff0c;可快速建立一个海量 0x02 漏洞概述 海…

翻译: Gen AI生成式人工智能学习资源路线图一

Introduction 介绍 本文档旨在作为学习现代人工智能系统背后的关键概念的手册。考虑到人工智能最近的发展速度&#xff0c;确实没有一个好的教科书式的资源来快速了解 LLMs 或其他生成模型的最新和最伟大的创新&#xff0c;但互联网上有大量关于这些主题的优秀解释资源&#x…

.NET周刊【6月第2期 2024-06-09】

国内文章 C#开源实用的工具类库&#xff0c;集成超过1000多种扩展方法 https://www.cnblogs.com/Can-daydayup/p/18230586 文章介绍了一个免费的C#工具类库Z.ExtensionMethods&#xff0c;可以通过NuGet包管理器轻松集成。该库支持.NET Standard 2.0和.NET Framework 4.0&am…

make和Cmake都有什么区别?(内附使用详解)

Make和Cmake make和Cmake的区别&#xff1a; 角色和功能: make: 是一个构建工具&#xff0c;它的任务是读取 Makefile 文件&#xff0c;并基于这些文件中的指令执行具体的构建操作。Makefile 文件包含了如何构建项目的规则&#xff0c;make 负责解析这些规则并执行必要的命令来…

webshell获取总结(cms获取方法、非cms获取方法、中间件拿Webshell方法)

目录 前期准备&#xff1a; 1、cookices靶场网站搭建&#xff1a; 2、dedecms靶场环境搭建&#xff1a; 获取Webshell方法总结&#xff1a; 一、CMS获取Webshell方法 二、非CMS获取Webshell方法 1、数据库备份获取Webshell 例如&#xff1a; 2、抓包上传获取Webshell 3、…

什么是APS计划排程? 企业产能与效率提升的智能引擎

快节奏和愈发激烈的现代工业环境中&#xff0c;如何提升生产效率、降低生产成本、最大化的满足订单交期、实现企业的降本增效&#xff0c;已成为众多企业关注的焦点。而APS系统——作为高级生产计划和调度系统的代表&#xff0c;正以其卓越的生产过程实时监控、产品加工过程优化…