编译器的结构

  计算机能读懂的语言是机器码,但对人来说由1和0组合的二进制序列既难写又难读。于是出现了用英文字母代表操作码的汇编语言,汇编语言是机器语言的符号化,汇编语言是面向机器的,使用汇编语言编程需要直接安排存储,规定寄存器、运算器的动作次序,汇编语言与计算机紧密相关,不同的计算机在指令长度、寻址方式、寄存器数目、指令表示等方面都不一样,由于汇编语言不便于进行数学描述,而且不可移植,于是出现了高级语言。高级语言是面向计算过程的和面向问题的语言,只与解题的步骤有关,而将高级程序设计语言"翻译"成机器语言的工作则是由编译程序来完成的。程序员的工作则是把要计算的问题化成高级程序设计语言的表达式、语句、过程/函数、对象,而不是机器指令序列。 
  把高级语言程序翻译成机器语言程序有两种做法:
编译和解释,相应的翻译工具也分别叫做编译器和解释器。以下分别讲述。
1.编译器工作原理
  编译器逐行扫描高级语言程序源程序,编译的过程如下: 
(1)
词法分析(Lexical Analysis)。识别关键字、字面量、标识符 (变量名、数据名)、运算符、注释行(给人看的,一般不处理)、特殊符号(续行、语句结束、数组)等六类符号,分别归类等待处理。
(2)
语法分析 (Syntax Analysis)。一个语句看作一串记号 (Token)流,由语法分析器进行处理。按照语言的文法检查判定是否是合乎语法的句子。如果是合法句子就以内部格式保存,否则报错。直至检查完整个程序。
(3)
语义分析 (Semantic Analysis)。语义分析器对各句子的语法做检查:运算符两边类型是否相兼容;该做哪些类型转换 (例如,实数向整数赋值要"取整");控制转移是否到不该去的地方;是否有重名或者使语义含糊的记号,等等。如果有错误,则转出错处理,否则可以生成执行代码。
(4)
中间代码生成。中间代码是向目标码过渡的一种编码,其形式尽可能和机器的汇编语言相似,以便下一步的代码生成。但中间码不涉及具体机器的操作码和地址码。采用中间码的好处是可以在中间码上做优化。
(5)
优化。对中间码程序做局部优化和全局 (整个程序)优化,目的是使运行更快,占用空间最小。局部优化是合并冗余操作,简化计算,例如x:=0可用一条"清零"指令替换。全局优化包括改进循环、减少调用次数和快速地址算法等。
(6)
代码生成。由代码生成器生成目标机器的目标码 (或汇编)程序,其中包括数据分段、选定寄存器等工作,然后生成机器可执行的代码。
  高级语言源程序经编译后得到目标码程序,还不能立即装入机器执行,因为程序中如果用到标准函数(它们生成的目标码已存放在模块库中),还需对编译后得到的目标模块进行连接。连接程序 (Linker)找出需要连接的外部模块,然后到模块库中找出被调用的模块,调入内存并连接到目标模块上,形成可执行程序。执行时,把可执行程序加载 (Loading)到内存中合适的位置 (此时得到的是内存中的绝对地址),就可执行了。其示意图为图1.1所示
    

图1.1 编译、连接和执行程序的过程

2.高级语言程序的解释执行 
  编译型语言由于可进行优化 ( 有的编译器可做多次优化 ) ,目标码效率很高,因此是目前软件开发的最主要编程语言。常见的程序设计语言,如 C/C++ 、 Pascal 、 FORTRAN 等都是编译型语言,用这些语言编写的源程序,都需要进行编译、连接,才能生成可执行程序。这对于大型程序、系统程序、支持程序来说是十分有利的,虽然编译时花费了不少时间,但程序的执行效率是很高的。不过,在有些场合,对程序的执行效率要求不高的场合,没有必要在编译上花费大量的时间,可以对高级语言源程序采取解释执行的方式。 
  解释执行需要有一个解释器 (Interpreter) ,它将源代码逐句读入。第一步先作词法分析,建立内部符号表;再作语法和语义分析,并作类型检查 ( 解释语言的语义检查一般比较简单,因为它们往往采用无类型或动态类型系统 ) 。完成检查后把每一语句压入执行堆栈,并立即解释执行。因为解释执行时只看到一个语句,无法对整个程序进行优化。但是解释执行占用空间很少。 
  操作系统的命令、Visual Basic、Java、JavaScript 都是解释执行的 ( 其中有些语言也可以编译执行 ) 。解释器不大,工作空间也不大,不过,解释执行难于优化、效率较低,这是这类语言的致命缺点 。
原文地址:https://www.cnblogs.com/OneDream/p/3075827.html