几个关键问题
- 什么是编译器?它和解释器的区别?
- 为什么要学习编译原理?
- 编译过程可以如何划分?各阶段的作用?
- 编译器是如何组织的?
- 编译器是如何产生的?
翻译器、编译器、解释器
- 翻译器:能够将一种语言编写的程序翻译成语义等价的另外一种语言的程序的软件系统。
- 编译器:翻译器的一种,通常其目标语言比源语言低级。
- 解释器:不产生目标代码,而是解释执行源程序。
【理解】翻译器相当于DeepL翻译,编译器能把C语言变成汇编语言,解释器可以根据程序和输入给出输出。
为什么要学习编译原理(了解)
- 能够确切地知道你所编写的程序是如何从高级语言程序变成可执行程序的,从而对编程有更深入的理解;进而可以提高纠错能力和编写高质量代码的能力。这种能力对大型软件的构造和维护尤为重要。
- 编译原理集中体现了计算机科学的很多核心思想:算法、数据结构,软件工程等,是其他领域的重要研究基础。编译器设计中的一些原理和技术足以应用到你今后职业生涯中的很多领域。
编译器的划分
- 前四阶段完成对源程序的分析,它们与源语言密切相关,因此也称为前端。
- 后三阶段完成对源语言的综合,由于与目标机器更相关,故也称后端。
各阶段的主要作用
词法分析
将字符流转换成记号流。
e.g. 源程序字符流:position = initial + rate * 60
经过此法分析器后变成:<id, 1> <=> <id, 2> <+> <id, 3> <*> <60>
并附带一个符号表:
position | … |
---|---|
initial | … |
rate | … |
语法分析
记号流经过语法规则变成树型抽象中间表示
e.g. initial + rate * 60的分析树:
语义分析
分析语法结果的含义和功能;检查程序语义的一致性;为代码生成阶段收集信息。
类型检查:
- 类型是否合法:例如,数组下标为整数
- 隐式类型转换
中间代码生成
在对源程序进行过语法和语义分析之后,很多编译器会显式地生成一种比较低级的中间表示,即中间代码。
中间表示必须具备特点:
- 易于产生
- 易于翻译成目标程序
代码优化
提高目标代码的质量。
目标代码生成
代码生成阶段:由中间代码生成目标代码。
- 为变量选择存储单元
- 把中间代码翻译成等价的机器指令序列
符号表管理
- 编译器的一项重要工作是记录并收集标识符及其各种属性,以提供标识符存储分配、类型、作用域等信息。
- 符号表为每个标识符保存一个记录数据结构,其中的域是标识符的各个属性。
- 该数据结构应允许编译器迅速找到一个名字的记录并读取和存储数据。
阶段的分组
编译器划分前端后端有利于移植及简化编译器的设计。
- 在编译器实现时,几个阶段的活动可能被组合起来形成一“遍”。
- 每一“遍”都会读取一个输入文件,并写一个输出文件。
现代高级语言大都需要多遍扫描才能完成从源程序到目标代码的转换。
编译器的生成
- X86机器上原有一个C编译器(C→X86),如何得到一个X86机器上的C++编译器(C++→X86)?
答:写一个(C++→X86)编译器的C源程序,在X86机器上用(C→X86)编译器编译即可。
- X86机器上原有一个C编译器(C→X86),如何得到一个MIPS机器上的C编译器(C→MIPS)?
答:
- 先写一个(C→MIPS)的C源程序
- 在X86机器上用(C→X86)编译器编译该源程序得到X86机器上的(CMIPS)编译器。
- 在X86机器上用刚生成的(C→MIPS)编译器再编译C源程序一次,即得到MIPS机器上的(C→MIPS)编译器。
- X86机器上没有任何编译器,如何得到一个C编译器?(自编译过程)
答:
- 先对C语言的核心部分构造一个很小的编译器(用低级语言构造)
- 用生成的这个编译器作为工具,继续构造一个稍大一些的编译器。
- 如此扩展下去,直到得到一个完整的C编译器。