前言:近期正在学b站上哈工大的编译原理课,但是越学越崩溃。所以干脆停止看视频,慢下来汇总、理清知识。我搜索了几篇关于编译原理的文章,读完后提取出有用的知识。当做入门吧,争取搞清why。[若侵权,请联系我删除~]
- “语言是怎么诞生的?为什么不能用一个语言解决所有问题?”
- 追寻程序设计语言的本质
- 从现实的方面来说,编译原理学过之后的益处(不考虑最后都没有入门的情况)包括:
1、可以更加容易的理解在一个语言中哪些写法是等价的,哪些是有差异的
2、可以更加客观的比较不同语言的差异
3、更不容易被某个特定语言的宣扬者忽悠
4、学习新的语言是效率也会更高
5、其实从语言a转换到语言b是一个通用的需求,学好编译原理处理此类需求时会更加游刃有余
零、编译总过程
整个流程包括预处理(Prepressing)、编译(Compilation)、汇编(Assembly)和链接(Linking)。
0.1 预编译
首先是源代码文件hello.c和相关头文件,如 stdio.h 被编译器 cpp预编译到一个 .i 文件。预编译过程主要是处理那些源代码文件中以 # 开始的预编译指令。比如#include 等。经预编译后的 .i 文件不包含任何宏定义,因为所有的宏已经被展开,并且包含的文件已经被插入到 .i 文件中。
0.2 编译
编译过程就是把预处理的文件经过一系列的词法分析、语法分析、语义分析、生成中间代码、生成目标代码 优化后生产相应的汇编文件代码。
0.3 汇编
汇编器将汇编代码转变成机器可以执行的指令,每一个汇编语句几乎都对应一条机器指令。所以汇编起的过程相对于编译器而言是比较简单的。因为没有复杂的语法,也没有予以,所以就不需要做指令优化,只是根据汇编指令和机器指令的对照便一一翻译就可以了。到这一步,经过预编译、编译和汇编就可直接输出目标文件。
由于汇编更接近机器语言,能够直接对硬件进行操作,生成的程序与其他的语言相比具有更高的运行速度,占用更小的内存,因此在一些对于时效性要求很高的程序、许多大型程序的核心模块以及工业控制方面大量应用。
0.4 链接
在一个目标文件中,不可能所有变量和函数都定义在同一个文件内部。不同文件之间要做相应的链接处理。
链接过程主要包括了:
- 地址和空间的分配(Address and Storage Alloction)
- 符号决议(Symbol Resolution)Ps:"决议"更倾向于静态链接,而"绑定"更倾向于动态链接。
- 重定位(Relocation)
链接分为:
- 静态链接:静态链接对于计算机内存和磁盘的空间浪费非常严重
- 动态链接:把链接这个过程推迟到了运行时再进行。所谓的动态链接表示重定位发生在运行时而非编译后。
虽然动态链接可以解决上述的两个问题,但是在性能上要略微比静态链接差一些。
让我们来看看什么是重定位。假设有个全局变量叫做 var ,它在目标文件A里面。我们在目标文件B里面要访问这个全局变量。由于在编译目标文件B的时候,编译器并不知道变量var的目标地址,所以编译器在没法确定的情况下,将目标地址设置为0,等待链接器在目标文件A和B连接起来的时候将其修正。这个地址修正的过程被叫做重定位,每个被修正的地方叫一个重定位入口。
链接器就是靠着重定位表来知道哪些地方需要被重定位的。每个可能存在重定位的段都会有对应的重定位表。在链接阶段,链接器会根据重定位表中,需要重定位的内容,去别的目标文件中找到地址并进行重定位。
一、翻译程序:编译器、解释器(将高级语言或汇编语言的代码转换成机器认识的代码)
机器不能理解我们用高级语言编写的代码,所以要在程序执行前将高级语言“翻译”为机器语言。这是一个将源语言程序转化为目标语言程序的过程,它依靠翻译程序来完成。
- 翻译程序包括:
编译器:将编译型语言(C++,Go)翻译为机器语言。(工作效率高,即时间快、空间省;交互性与动态性差,可移植性差)