在一个程序可以运行之前,他首先需要被 编译器翻译成一种能够被计算机执行的形式。
编译器就是完成这项翻译工作的 软件系统。
一、语言处理器
语言处理器有两种: 编译器 和 解释器
- 编译器:
一个编译器就是一个程序。
作用:他可以阅读以某一种语言(源程序)编写的程序,并把该程序翻译成一个等价的、用另一种语言(目标语言)编写的程序。
如果目标程序是一个可执行的机器语言处理器,可以被用户调用,处理输入并产生输出。
- 解释器:
解释器直接利用用户提供的输入执行源程序中指定的操作(并不通过翻译的方式生成目标程序)。
二者比较:
- 由编译器产生的机器语言目标程序通常比解释器快
- 解释器的错误诊断效果比编译器号,它逐个语句地执行源程序。
一个源程序可能被分割成多个模块,并存放于独立文件中。
完整创建可执行目标程序的过程:
- 预处理器:把源程序聚合在一起,并把“宏”的缩写形式转换为源语言的语句;进行预处理后,将源程序传递给编译器。
- 编译器:产生一个汇编语言程序;传递给汇编器。
- 汇编器:汇编语言程序由汇编器进行处理,生成可重定位代码;传递给链接器。
- 链接器:一个文件中的代码可能指向另一个文件中的位置,链接器解决该外部内存地址的问题。大型程序经常被分成多个部分进行编译,可重定位的机器代码有必要和其他可重定位的目标文件以及库文件连到一起,形成真正在机器上运行的代码。
- 加载器:把所有的可执行文件放到内存中执行。
二、编译器结构
编译器能把源程序映射为在语义上等价的目标程序。
这个映射过程由两部分组成:
- 分析部分:称为编译器的前端(front end)
- 综合部分:称为编译器的后段(back end)
分析部分:将源程序分解成多个组成要素并加上语法结构。创建该源程序的一个中间表示。分析检查出语法构成等方面的错误,需提示用户改正;收集有关源程序的信息,存放在符号表的数据结构中。
符号表和中间表示形式一起传送给综合部分。
综合部分:根据中间表示和符号表中的信息来构造用户期待的目标程序。
词法分析
编译器的第一个步骤。
词法分析(lexical analysis)也称为扫描(scanning)。
- 词法分析器读入组成源程序的字符流,并将它们组织成有意义的词素(lexeme)的序列。
- 每个词素,词法分析器产生
<token-name, attribute-value>
形式的词法单元(token)输出,传递给语法分析。
语法分析
编译器的第二个步骤。
语法分析(syntax analysis)也称为解析(parsing)。
- 语法分析器使用由词法分析器生成的各个词法单元的第一个分量来创建树形的中间表示。
- 该中间表示给出了词法分析产生的词法单元流的语法结构
- 常用表示方式语法树(syntax tree),书中每个内部节点表示一个运算,结点的字结点表示该运算的分量
语义分析
语义分析器 使用语法树和 符号表 中的信息检查源程序是否和语言定义的语义一直。
同时收集类型信息,并存放在语法树和符号表中,在后续中间代码生成过程中使用。
语义分析作用:
- 类型检查(type checking):检查每个运算符是否具有匹配的运算分量
- 自动类型转换(coercion):程序设计语言可能允许某些类型的转换
中间代码生成
可能构造出一个或多个中间表示;
中间表示有多种形式,其中有语法树。
中间表示具有两个重要性质:
- 易于生成
- 能轻松被翻译为目标机器上的语言
代码生成
代码生成器以源程序的中间表示形式作为输入,将其映射到目标语言。
中间指令被翻译成能够完成相同任务的机器指令序列。
重要方面:
- 合理分配寄存器以存放变量的值。
三、程序设计语言基础
静态和动态的区别:
- 一个语言使用的策略支持编译器静态决定某个问题, 该语言使用了一个静态(static)策略,此问题可在编译时刻(compile time)决定;
- 一个只允许在运行程序的时候作出决定的策略被称为动态策略(dynamic policy),需要在运行时刻(run time)作出决定
声明的作用域:
如果仅通过阅读程序就可以确定一个声明的作用域,那么这个语言使用的是静态作用域(static scope),或者说词法作用域(lexical scope);
否则,这个语言使用动态作用域(dynamic scope),程序运行时,同一个x的使用会指向x的几个声明中的某一个。
环境与状态:
- 环境是一个从名字到存储位置的映射。因为变量就是指内存的位置
- 状态是一个从内存位置到它们的值的映射。
环境改变需要遵守语言的作用域规则。
参数传递:参数可以通过值或引用的方式从调用过程传递给被调用过程。当通过值传递方式传递大型对象时,实际被传递的值是指向这些对象本身的引用。这样就变成了一个高效的引用调用。
别名:当参数被以引用传递方式(高效地)传递时,两个形式参数可能会指向同一个对象。这会造成一个变量的修改改变了另一个变量的值。