《编译原理》(第二版)第一章的学习笔记(一)
Java语言处理器结合了编译和解释过程,如图1.4所示
一个Java源程序首先被编译成一个称为字节码(bytecode)的中间表示形式。然后 一个虚拟机对得到的字节码加以解释执行。这样安排的好处之一是在一台机器上编译得到的字节码可以在另一台机器上解释执行。通过网络就可以完成机器之间的迁移。
为了更快地完成输入到输出的处理,有些被称为即时(just in time)编译器的Java编译器在运行中间程序处理输入的前一刻首先把字节码翻译成机器语言,然后再执行程序。
如图1.5所示,除了编译器之外,创建一个可执行的目标程序还需要一些其他程序。一个源程序可能被分割成为多个模块,并存放于独立的文件中。把源程序聚合在一起的任务有时会由一个被称为预处理器(preprocessor)的程序独立完成。预处理器还负责把那些称为宏的缩写形式转换为源语言的语句。
然后,将经过预处理的源程序作为输入传递给一个编译器。编译器可能产生一个汇编语言程序作为其输出,因为汇编语言容易输出和调试。接着,这个汇编语言程序由称为汇编器(assemble)的程序进行处理,并生成可重定位的机器代码。
大型程序经常被分成多个部分进行编译,因此,可重定位的机器代码有必要和其他可重定位的目标文件以及库文件连接在一起,形成真正在机器上运行的代码。一个文件中的代码可能指向另一个文件的位置,而链接器(Linker)能够解决外部内存地址的问题。最后,加载器(Loader)把所有的可执行目标文件放到内存中执行。
到现在为止,我们把编译器看作一个黑盒子,它能够把源程序映射为在语义上等价的目标程序。如果把这个盒子稍微打开一点,我们就会看到映射过程由两部分组成:分析部分和综合部分。
分析(analysis)部分把源程序分解成为多个组成因素,并在这些要素之上加上语法结构。然后,它使用这个结构来创建该源程序的一个中间表示。如果分析部分检查出源程序没有按照正确的语法构成,或者语义上不一致,它就必须提供有用的信息,使得用户可以按此进行改正。分析部分还会收集有关源程序的信息,并把信息存放在一个称为符号表(symbol table)的数据结构中。符号表将和中间表示形式一起传送给综合部分。
综合(synthesis)部分根据中间表示和符号表中的信息来构造用户期待的目标程序。分析部分经常被称为编译器的前端(front end),而综合部分称为后端(back end)。
如果我们更加详细地研究编译过程,会发现它顺序执行了一组步骤(phase)。每个步骤把源程序的一种表现形式转化为另一种表现形式。一个典型的把编译程序分解成为多个步骤的方式如图1-6所示。在实践中,多个步骤可能组合在一起,而这些组合在一起的步骤之间的中间表示不需要被明确地构造出来。存放整个源程序的信息的符号表可由编译器的各个步骤使用。
有些编译器在前端和后端之间有一个与机器无关的优化步骤。这个优化步骤的目的是在中间表示之上进行转换,以便后端程序能够生成更好的目标程序。如果基于未经过此优化步骤的中间表示来生成代码,则代码的质量会受到影响。因为优化是可选的,所以图1-6中所示的两个优化步骤之一可以被省略。