第一章 引论
什么是编译器?
:一个程序,读入以某一种语言(源语言)编写的程序,翻译为等价的,用另一种语言(目标语言)编写的程序
编译器:源程序-编译器-目标程序
编译器vs解释器:是否需要在某种虚拟机中运行,是否一行一行处理
编译器优点:
- 效率高,一次编译多次运行
- 通常目标程序是可执行的
Java:结合了编译器和解释器,使用Java虚拟机,而不是生成可执行文件,只要安装了JRE(Java运行时环境)就可以在不同平台上运行
C语言的编译:
预处理器(加了井号的东西,如#define)-编译器-汇编器-链接器-加载器
编译器的结构
- 分析部分(Analysis):编译器前端,搜集源程序相关信息,放入符号表,定位错误信息(语法语义等),是与机器无关的部分
- 综合部分(Synthesis):编译器后端,根据符号表和中间表示构造目标程序,与机器相关的部分
每个步骤把源程序的一种表述方式转换成另一种表示方式
符号表:
记录源程序中使用的变量的名字,收集各种属性,包括:
- 名字的存储分配
- 类型
- 作用域
- 过程名字的参数数量、参数类型等
可由编译器的各个步骤使用
词法分析:
读入字符流,输出有意义的词素(lexeme),产生词法单元
词法单元:<token-name,attribute-value>
- token-name由语法分析步骤使用
- attribute-value指向相应的符号表条目
例如:position=initial+rate*60
1 | <id,1><=,><id,2><+,><id,3><*,><number,4> |
语法分析
需要得到词素序列的语法结构
语法分析/解析:根据词法单元的第一个分量来创建树形中间表示形式,通常是语法树
指出词法单元流的语法结构
语义分析
得到语义,对于编译器来说比较难
使用语法树和符号表中的信息,检查源程序是否满足语言定义的语义约束
中间代码生成
根据语义分析的输出,生成类机器语言的中间表示(可以进行中间代码优化)
代码优化
通过对中间代码的分析,改进中间代码,得到更好的目标代码
代码生成
把中间表示形式映射到目标语言
编译器的趟(Pass)
以文件为输入输出单位的编译过程的个数,每趟可由一个或若干个步骤构成(要将源程序文件读入多少次)
程序设计语言
第一代:机器语言;第二代:汇编语言;第三代:高级语言;第四代:特定应用语言;第五代:基于逻辑和约束的语言
命令式语言:指明如何完成;声明式语言:指明要完成哪些计算
基础概念
1、静态/动态
- 静态:不需要运行程序,只需要在编译时就可以获得状态(编译时就可以确定变量的数据类型,例如Java,C++)
- 动态:需要运行这个程序才可以获得的状态(变量的类型在运行时确定,例如Python,JavaScript)
例如:Java类声明中的static指明了变量的位置可静态确定
2、作用域
变量有效的范围
- 静态作用域:大部分都是静态作用域,通常可以根据源代码直接看出来它的作用域(块作用域)
- 动态作用域:需要运行时才可以确定,取决于调用的函数或过程,已经被淘汰
3、参数传递机制
值传递:对实在参数求值/拷贝,再存放到被调用过程的形参的内存位置上
引用调用:实际传递的是实在参数的地址
名调用:基本废弃
4、别名
两个指针指向同一个位置的情况