C 语言的代码首先由预处理器(preprocessor)对 #include 和 #define 进行处理。具体来说,读入头文件,将所有的宏展开,这就是预处理(preprocess)。
接着,编译器对预处理器的输出进行编译,生成汇编语言(assemble language)的代码。一般来说,汇编语言的代码的文件扩展名是“.s”。
然后,汇编语言的代码由汇编器(assembler)转换为机器语言,这个处理过程称为汇编 (assemble)。
汇编器的输出称为目标文件(object file)。一般来说,目标文件的扩展名是“.o”。
将目标文件转换为最终可以使用的形式的处理称为链接(link)。使用程序库的情况下,会在这个阶段处理程序库的加载。
生成可执行文件的过程
说到“静态”,就是指不运行程序而进行某些处理;说到“动态”,就是指一边运行程序一边进行某些处理。
狭义的编译大致可分为下面 4 个阶段。
-
语法分析 首先要对代码进行解析,将其转换为计算机易于理解的形式。这里 的解析(parse)也称为语法分析(syntax analyzing)。解析代码的程序模块称为解析器(parser) 或语法分析器(syntax analyzer)。那么“易于计算机理解的形式”究竟是怎样的形式呢?那就是称为语法树(syntax tree)的 形式。
语法树 -
语义分析 通过解析代码获得语法树后,接着就要解析语法树,除去多余的内容,添加必要的信息, 生成抽象语法树(Abstract Syntax Tree,AST)这样一种数据结构。上述处理就是语义分析(semantic analysis)。
-
生成中间代码 生成抽象语法树后,接着将抽象语法树转化为只在编译器内部使用的中间代码 (Intermediate Representation,IR)。之所以特地转化为中间代码,主要是为了支持多种编程语言或者机器语言。
-
代码生成 最后把中间代码转换为汇编语言,这个阶段称为代码生成(code generation)。负责代码生 成的程序模块称为代码生成器(code generator)。
除了之前讲述的 4 个阶段之外,现实的编译器还包括优化(optimization)阶段。
现在的计算机,即便是同样的代码,根据编译器优化性能的不同,运行速度也会有数倍的 差距。由于编译器要处理相当多的程序,因此在制作编译器时,最重要的一点就是要尽可能地 提高编译出来的程序的性能。
优化可以在编译器的各个环节进行。可以对抽象语法树进行优化,可以对中间代码的代码 进行优化,也可以对转换后的机器语言进行优化。进一步来说,不仅是编译器,对链接以及运 行时调用的程序库的代码也都可以进行优化。
网友评论