1、第1章 编译概述,知识点:翻译和解释的概念编译的阶段、任务、及典型结构编译程序的伙伴工具,2/60,编译概述,简介 1.1 翻译和解释 1.2 编译的阶段和任务 1.3 编译有关的其他概念 1.4 编译程序的伙伴工具 1.5 编译原理的应用小结,3/60,简介,什么是编译? 把源程序转换成等价的目标程序的过程即是编译。 编译程序的设计涉及到的知识: 程序设计语言 形式语言与自动机理论 计算机体系结构 数据结构 算法分析与设计 操作系统 软件工程等,4/60,1.1 翻译和解释,一、程序设计语言 二、翻译程序,5/60,一、程序设计语言,低级语言 机器语言 符号语言 汇编语言高级语言 过程性语言
2、面向用户的语言 如:C、Pascal 专用语言面向问题的语言 如:SQL 面向对象的语言 如:Java、C+,6/60,高级语言的优点,高级语言独立于机器。所编程序移植性比较好。 不必考虑存储单元的分配问题,数据的外部形式转换成机器的内部形式等细节。 用变量描述存储单元 具有丰富的数据结构和控制结构。 数据结构:数组、记录等 控制结构:循环、分支、过程调用等。 更接近于自然语言。 可读性好,便于维护。 编程效率高。,7/60,二、翻译程序,翻译程序扫描所输入的源程序,并将其转换为目标程序,或将源程序直接翻译成结果。,编译器(即编译程序) 把源程序翻译成目标程序的翻译器 解释器(即解释程序) 直
3、接执行源程序的翻译器,8/60,编译程序,源程序是用高级语言或汇编语言编写的,目标程序是用目标语言表示的。,9/60,编译和执行阶段,编译时间:实现源程序到目标程序的转换所占用的时间。 源程序和数据是在不同时间(即分别在编译阶段和运行阶段)进行处理的。,源程序,目标程序,编译时,数据,执行时,10/60,解释程序,解释程序解释执行源程序,不生成目标程序 同时处理源程序和数据 一种有效的方法:先将源程序转换为某种中间形式,然后对中间形式的程序解释执行。,源程序,数据,11/60,total:=total+rate*4 的解释过程,解释程序先将源程序转换成一棵树,遍历该树,执行结点上所规定的动作。
4、,调用一个过程,执行右边的表达式,计算结果送入total的存储单元,递归调用过程,对表达式进行计算,12/60,1.2 编译的阶段和任务,一、分析阶段根据源语言的定义,分析源程序的结构 1.词法分析 2.语法分析 3.语义分析 二、综合阶段根据分析结果构造出所要求的目标程序 4.中间代码生成 5.代码优化 6.目标代码生成 三、符号表的管理 四、错误诊断和处理,13/60,编译程序的典型结构,14/60,一、分析阶段,任务:根据源语言的定义,对源程序进行结构分析和语义分析,从而把源程序正文转换为某种内部表示。 分析阶段是对源程序结构的静态分析。 任务划分: 1.词法分析 2.语法分析 3.语义
5、分析,15/60,1. 词法分析,扫描,线性分析 词法分析器: 依次读入源程序中的每个字符,对构成源程序的字符串进行分解,识别出每个具有独立意义的字符串作为记号(token)并组织成记号流。 把需要存放的单词放到符号表中,如变量名,标号,常量等。 形成记号的字符串叫做该记号的单词(lexeme)。 工作依据:源语言的构词规则(即词法),也称为模式(pattern)。 标识符的模式是:以字母开头的字母数字序列。,16/60,对 total:=total+rate*4 的词法分析,(1) 标识符 total (2) 赋值号 := (3) 标识符 total (4) 加号 + (5) 标识符 rat
6、e (6) 乘号 * (7) 整常数 4,17/60,空格、注释的处理及其他,分隔记号的空格:被删去 源程序中的注释:被跳过 识别出来的标识符要放入符号表。 对某些记号还要增加一个“属性值” 如发现标识符total时,词法分析器不仅产生一个记号如id,还把它的单词total填入符号表(如果total在表中不存在的话),记号id的属性值就是指向符号表中R条目的指针。,18/60,2. 语法分析,层次结构的分析 把记号流按语言的语法结构层次地分组,以形成语法短语。 源程序的语法短语常用分析树表示 工作依据:源语言的语法规则 程序的层次结构通常由递归的规则表示,如表达式的定义如下: (1) 任何一个
7、标识符是一个表达式 (2) 任何一个数是一个表达式 (3) 如果expr1和expr2是表达式,expr1+expr2、 expr1*expr2、(expr1)也都是表达式。,19/60,total:=total+rate*4 的分析树,语法树:,20/60,语句的递归定义,如果id是一个标识符,expr是一个表达式,则 id:=expr 是一个语句。如果expr是表达式,stmt是语句,则 while (expr) do stmt 和if (expr) then stmt 都是语句。,21/60,3. 语义分析,对语句的意义进行检查分析 收集类型等必要信息 用语法分析确定的层次结构表示各语法
8、成份 工作依据:源语言的语义规则 一个重要任务:类型检查 根据规则检查每个运算符及其运算对象是否符合要求; 数组的下标是否合法; 过程调用时,形参与实参个数、类型是否匹配等,22/60,赋值语句 total:=total+rate*4 插入转换符的语法树,23/60,total:=total+rate*4 的各分析步骤及其中间结果,total:=total+rate*4,id1:=id1+id2*4,24/60,二、综合阶段,任务:根据所制定的源语言到目标语言的对应关系,对分析阶段所产生的中间形式进行综合加工,从而得到与源程序等价的目标程序。 任务划分: (4) 中间代码生成 (5) 代码优化
9、 (6) 目标代码生成,25/60,4. 中间代码生成,中间代码:一种抽象的机器程序 中间代码应具有两个重要的特点: 易于产生 易于翻译成目标代码 中间代码有多种形式 三地址代码具有的特点: 每条指令除了赋值号之外,最多还有一个运算符。 编译程序必须生成临时变量名,以便保留每条指令的计算结果。 有些“三地址”指令少于三个操作数,26/60,total:=total+rate*4 的三地址代码,temp1:=inttoreal(4) temp2:=id2*temp1 temp3:=id1+temp2 id1:=temp3,27/60,5. 代码优化,对代码进行改进,使之占用的空间少、运行速度快。
10、,什么是代码优化?,代码优化首先是在中间代码上进行的。,temp1:=id2*4.0 temp2:=id1+temp1 id1:=temp2,“优化编译程序”: 能够完成大多数优化的编译程序,temp1:=inttoreal(4) temp2:=id2*temp1 temp3:=id1+temp2 id1:=temp3,temp1:=id2*4.0 id1:=id1+temp1,28/60,6. 目标代码生成,生成的目标代码一般是可以重定位的机器代码或汇编语言代码。 涉及到的两个重要问题: 对程序中使用的每个变量要指定存储单元 对变量进行寄存器分配,29/60,total:=total+rat
11、e*4 的目标代码,MOVF id2, R2 MULF #4.0, R2 MOVF id1, R1 ADDF R2, R1 MOVF R1, id1,30/60,赋值语句total:=total+rate*4的各综合步骤及结果,temp1:=inttoreal(4) temp2:=id2*temp1 temp3:=id1+temp2 id1:=temp3,temp1:=id2*4.0 id1:= id1+trmp1,MOVF id2, R2 MULF #4.0, R2 MOVF id1, R1 ADDF R2, R1 MOVF R1, id1,31/60,total:=total+rate*4
12、 的翻译过程,total:=total+rate*4,temp1:=inttoreal(4) temp2:=id2*temp1 temp3:=id1+temp2 id1:=temp3,temp1:=id2*4.0 id1:= id1+trmp1,MOVF id2, R2 MULF #4.0, R2 MOVF id1, R1 ADDF R2, R1 MOVF R1, id1,id1:=id1+id2*4,32/60,三、符号表管理,编译程序的一项重要工作: 记录源程序中使用的标识符 收集每个标识符的各种属性信息 符号表是由若干记录组成的数据结构 每个标识符在表中有一条记录 记录的域是标识符的属性
13、 对符号表结构的要求: 应允许快速地找到标识符的记录 可在其中存取数据 标识符的各种属性是在编译的各个不同阶段填入符号表的。,33/60,声明语句:float total, rate;,词法分析器: float是关键字 total、rate是标识符 在符号表中创建这两个标识符的记录 语义分析器: total、rate都表示变量 float表示这两个变量的类型 可以把这两种属性及存储分配信息填入符号表。 在语义分析和生成中间代码时,还要在符号表中填入对这个float型变量进行存储分配的信息。,34/60,四、错误处理,在编译的每个阶段都可能检测到源程序中存在的错误 词法分析程序可以检测出非法字符
14、错误。 语法分析程序能够发现记号流不符合语法规则的错误。 语义分析程序试图检测出具有正确的语法结构,但对所涉及的操作无意义的结构。 代码生成程序可能发现目标程序区超出了允许范围的错误。 由于计算机容量的限制,编译程序的处理能力受到限制而引起的错误。 处理与恢复 判断位置和性质 适当的恢复,35/60,1.3 编译有关的其他概念,一、前端和后端 二、“遍”,36/60,一、前端和后端,前端主要由与源语言有关而与目标机器无关的那些部分组成 词法分析、语法分析、符号表的建立、语义分析和中间代码生成 与机器无关的代码优化工作 相应的错误处理工作和符号表操作 后端由编译程序中与目标机器有关的部分组成 与
15、机器有关的代码优化、目标代码的生成 相应的错误处理和符号表操作 把编译程序划分成前端和后端的优点: 便于移植、便于编译程序的构造。,37/60,二、遍,一“遍”是指对源程序或其中间形式从头到尾扫描一遍,并作相关的加工处理,生成新的中间形式或目标程序。 编译程序的结构受“遍”的影响 遍数 分遍方式一遍扫描的编译程序 多遍编译程序,38/60,一遍扫描的编译程序,语法分析器,词法分析器,语义分析及代码生成,控制流 信息流,开始,结束,整理目标程序,39/60,多遍编译程序,40/60,编译程序分遍的优缺点,分遍的主要好处: 可以减少对主存容量的要求 可使各遍编译程序功能独立、单纯,相互联系简单,编
16、译程序结构清晰。 能够实现更充分的优化工作,获得高质量目标程序。 通过分遍将编译程序的前端和后端分开,可以为编译程序的移植创造条件。分遍的缺点: 增加了不少重复性的工作。,41/60,1.4 编译程序的伙伴工具,42/60,一、预处理器 二、汇编程序 三、连接装配程序,编译程序的前后处理器,43/60,一、预处理器,预处理器的主要功能: 1.宏处理 2.文件包含 3.语言扩充,44/60,1. 宏理器,预处理器允许用户在源程序中定义宏。 C语言源程序中的一个宏定义:#define prompt(s) fprintf(stderr, s) 宏处理器处理两类语句,即宏定义和宏调用。 宏定义通常用统
17、一的字符或关键字表示,如define或macro,宏定义由宏名字及宏体组成,通常宏处理器允许在宏定义中使用形参。宏调用由调用宏的命令名(宏名)和所提供的实参组成。宏处理器用实参代替宏体中的形参,再用变换后的宏体替换宏调用本身。,45/60,2. 文件包含,预处理器把文件的包含声明扩展为程序正文。C语言程序中的“头文件”包含声明行:#include 预处理器处理到该语句时,就用文件stdio.h的内容替换此语句。,46/60,3. 语言扩充,有些预处理器用更先进的控制流和数据结构来增强原来的语言。例如: 预处理器可以将类似于while或if-then-else语句结构的内部宏提供给用户使用,而这
18、些结构在原来的程序设计语言中是没有的。 当程序中使用了这样的结构时,由预处理器通过宏调用实现语言功能的扩充。,47/60,二、汇编程序,汇编语言用助记符表示操作码,用标识符表示存储地址。 赋值语句b:=a+2相应的汇编语言程序为:MOV a, R1ADD #2, R1MOV R1, b 最简单的汇编程序对输入作两遍扫描。,48/60,第一遍,找出标志存贮单元的所有标识符,并将它们存贮到汇编符号表中。 汇编符号表独立于编译程序的符号表 在符号表中指定该标识符所对应的存储单元地址,此地址是在首次遇到该标识符时确定的。假定一个字包括4个字节,每个变量占一个字,完成第一遍扫描后,得到汇编符号表:标识符
19、 地址 a 0 b 4,49/60,第二遍,把每个用助记符表示的操作码翻译为二进制表示的机器代码。 把用标识符表示的存储地址翻译为汇编符号表中该标识符所对应的地址。输出通常是可重定位的机器代码。 起始地址为0,各条指令及其所访问的地址都是相对于0的逻辑地址。 当装入内存时,可以指定任意的地址L作为开始单元。 输出中要对那些需要重定位的指令做出标记 标记供装入程序识别,以便计算相应的物理地址。,50/60,可重定位机器代码,假定:0001 代表 MOV S, R0011 代表 ADD0010 代表 MOV R, D 假定机器指令的格式为:操作符 寄存器 寻址模式 地址第二遍输出的可重定位机器代码
20、:0001 01 00 00000000*0011 01 10 000000100010 01 00 00000100*,b:=a+2的汇编代码 MOV a, R1ADD #2, R1MOV R1, b,51/60,绝对机器代码,假如装入内存的起始地址为L=00001111 则a和b的地址分别是15和19 则装入后的机器代码为:0001 01 00 000011110011 01 10 000000100010 01 00 00010011,可重定位机器代码:0001 01 00 00000000*0011 01 10 000000100010 01 00 00000100*,52/60,三、
21、连接装配程序,装入 读入可重定位的机器代码 修改重定位地址 把修改后的指令和数据放在内存的适当地方或形成可执行文件连接 把几个可重定位的机器代码文件连接成一个可执行的程序,53/60,1.5 编译原理的应用,语法制导的结构化编辑器 程序格式化工具 软件测试工具 程序理解工具 高级语言的翻译工具,54/60,语法制导的结构化编辑器,具有通常的正文编辑和修改功能 能象编译程序那样对源程序进行分析,把恰当的层次结构加在程序上。 可以保证源程序 无语法错误 有统一的可读性好的程序格式 结构化编辑器能够执行一些对编制源程序有用的附加任务,如: 检查用户的输入是否正确 自动提供关键字 从BEGIN或左括号
22、跳到与其相匹配的END或右括号,55/60,程序格式化工具,读入并分析源程序 使程序结构变得清晰可读,如: 用缩排方式表示语句的嵌套层次结构 用一种专门的字型表示注释等,56/60,软件测试工具,静态测试动态测试,动态测试器,静态测试器,读入源程序 在不运行该程序的情况下对其进行分析,以发现程序中潜在的错误或异常。不可能执行到的死代码 未定义就引用的变量 试图使用一个实型变量作为指针等。,利用测试用例实际执行程序 记录程序的实际执行路线 将实际运行结果与期望结果进行比较,以发现程序中的错误或异常。,57/60,程序理解工具,对源程序进行分析 确定各模块间的调用关系 记录程序数据的静态属性和结构属性 画出控制流程图,58/60,高级语言的翻译工具,将用某种高级语言开发的程序翻译为另一种高级语言表示的程序,59/60,小 结,什么是编译 翻译程序: 编译程序(编译程序、汇编程序)、解释程序 二者的异同 编译系统(编译环境) 编译程序的伙伴工具、功能及工作原理 预处理器 汇编程序 连接装配程序,60/60,小 结(续),编译程序的各组成部分及其功能 词法分析 语法分析 语义分析 中间代码生成 代码优化 目标代码生成 前端和后端 遍 编译程序的设计涉及到的知识,