C++ 编译器是怎么工作的
小零碎的东西
cin.get()
文件-->可执行文件
#include <iostream>
1、预处理语句,会在编译文件之前被评估。将iostream文件里的所有内容拷贝
到这个文件里
2、文件编译,代码转为实际的机器码
x86 就是定位于32位的windows
所有.cpp文件都会被编译,而头文件则不会
记住header file 通过预处理被include到cpp文件里,那是它们被编译的时候
每个cpp文件会被编译成一个object文件,扩展名的话,用windows编译器是.obj
当我们有了一个个obj文件,也就是cpp文件被编译后的结果,我们就有办法把他们联系起来组成一个exe,这时候就是linker(连接器)的用武之处了
error list
error list 的工作原理其实是,解析(parse)output窗口,找error关键字,然后从那找信息,放入list
编译器是怎么工作的
我们用文本来写C++,就是text而已,我们需要某种方法将文本转化成应用,从而在计算机运行。从text到可执行binary,基本上有两个主要操作需要发生。一个是compoling(编译),一个是linking(连接)。
C++编译器唯一要做的就是把文本变为中继格式——obj。然后obj们会传入linker,linker会做所有linking的事。
在compiler产出这些obj时,其实它有做好几件事。首先,它需要pre-process(预处理)我们的代码,也就是所有Preprocessor语句在那时被评估。被预处理后,我们会进入tokenizing(标记解释)和parsing(解析)阶段。基本就是把这C++文本处理成编译器能懂和处理的语言。基本上结果就是创建了某种叫做abstract syntax tree(抽象语法树),也就是代码的表达,但是是以抽象语法树的形式。
说到底编译器的工作也就是把代码转化成要么是constant data(常数资料)要么是instruction(指令)。
当编译器创建了这颗语法树之后,就可以开始产生代码了,这个代码是真正的cpu会执行的代码。同时,我们也会得到一些其他数据,比如某个地方存储着我们所有的constant variables(常数变量)。这基本就是编译器的工作了,并不非复杂。
编译器给每个cpp,也就是每个translation unit生成了obj。项目里的每个cpp,都会被编译器编译成一个obj,这些cpp文件也叫translation unit(编译单元)。
本质上你得意识到c++根本不在乎文件,文件这种东西在c++里不存在。
举例说,java里,你的class名必须跟文件名相同,而你的文件夹架构也得跟package一样。之所以如此是因为java需要某些文件存在,c++完全不是这回事。没有文件这种东西,文件知识用来给编译器提供源码的某种方法。你要告诉编译器文件类型和编译器该如何处理它。(.cpp .c .h)
文件不代表任何东西。我们传给编译器每个c++文件,我们告诉它这是c++,请按c++编译。它就会把它当成一个translation unit,然后ranslation unit会得到一个obj。
在cpp文件include其它cpp有时候其实是很常见的作法,然后你只编译那一个cpp文件,也就是只有一个translation unit,也就只有一个obj。
这也就是为什么会有translation unit,cpp两个术语
pre-processing
编译第一阶段 preprocessing,在这期间,编译器就是检查所有的pre-processing语句并评估。常见的有include,define,if和ifdef。它们同样也是pragma语句,就是告诉编译器具体要做什么。
#include
:你只要指定你要include哪个文件,然后preprocessor会打开文件,读取所有内容,然后黏贴进你写的include的哪个文档。
![](https://img.haomeiwen.com/i1889272/8574c71575bfc8d5.png)
让编译器输出一个文档,里面包含所有这些preprocessor评估的结果
.i
文件
g++ -c -E
define
:就是会去搜索这个词,然后替换成后面的东西。
if
:让我们依据特定条件包含或者剔除代码
当preprocessor结束后,我们就可以将C++代码编译成机器码了。
obj
可读一点的格式,asm
DEBUG
debug是不会做任何优化的,而且会有很多额外的东西,以确保我们的代码尽可能冗长以及尽可能易于debug
常数折叠
constant folding ,任何常量都可以在编译时计算出来。
函数签名
需要独一无二的定义你的函数。当我们有多个obj,我们的函数会在多个obj中被定义。linker会负责将他们联系起来,它怎么做呢,就是会查找这个函数签名。
Call指令
当你调用函数时,编译器就会生成一个call指令