
对于这种伪指令,预编译所要做的是将程序中的所有 a 用 b 替换,但作为字符串常量的 a
则不被替换。还有 #undef ,则将取消对某个宏的定义,使以后该串的出现不再被替换。
(2) 条件编译指令,如 #ifdef , #ifndef , #else , #elif , #endif 等。
这些伪指令的引入使得程序员可以通过定义不同的宏来决定编译程序对哪些代码进行处理
。
预编译程序将根据有关的文件,将那些不必要的代码过滤掉
(3) 头文件包含指令,如 #include "FileName" 或者 #include <FileName> 等。
在头文件中一般用伪指令 #define 定义了大量的宏 ( 最常见的是字符常量 ) ,同时包含有各种
外部符号的声明。采用头文件的目的主要是为了使某些定义可以供多个不同的 C 源程序使
用。因为在需要用到这些定义的 C 源程序中,只需加上一条 #include 语句即可,而不必再
在此文件中将这些定义重复一遍 。 预编译程序将把头文件中的定义统统都加入到它所产生的
输出文件中 , 以供编译程序对之进行处理 。 包含到 C 源程序中的头文件可以是系统提供的
,
这些头文件一般被放在 /usr/include 目录下。在程序中 #include 它们要使用尖括号 (<>) 。另
外开发人员也可以定义自己的头文件,这些文件一般与 C 源程序放在同一目录下,此时在
#include 中要用双引号 ("") 。
(4) 特殊符号,预编译程序可以识别一些特殊的符号。
例如在源程序中出现的 LINE 标识将被解释为当前行号 ( 十进制数 ) , FILE 则被解释为当前被
编译的 C 源程序的名称 。 预编译程序对于在源程序中出现的这些串将用合适的值进行替换 。
预编译程序所完成的基本上是对源程序的 “ 替代 ” 工作 。 经过此种替代 , 生成一个没有宏定义
、
没有条件编译指令 、 没有特殊符号的输出文件 。 这个文件的含义同没有经过预处理的源文件
是相同的 , 但内容有所不同 。 下一步 , 此输出文件将作为编译程序的输出而被翻译成为机器
指令。
第二个阶段编译、优化阶段。经过预编译得到的输出文件中,只有常量;如数字、字符串
、
变量的定义,以及 C 语言的关键字,如 main,if,else,for,while,{,}, +,-,*,\ 等等。
编译程序所要作得工作就是通过词法分析和语法分析 , 在确认所有的指令都符合语法规则之
后,将其翻译成等价的中间代码表示或汇编代码。
优化处理是编译系统中一项比较艰深的技术。它涉及到的问题不仅同编译技术本身有关 , 而
且同机器的硬件环境也有很大的关系 。 优化一部分是对中间代码的优化 。 这种优化不依赖于
具体的计算机。另一种优化则主要针对目标代码的生成而进行的。
对于前一种优化,主要的工作是删除公共表达式、循环优化 ( 代码外提、强度削弱、变换循
环控制条件、已知量的合并等 ) 、复写传播,以及无用赋值的删除,等等。
后一种类型的优化同机器的硬件结构密切相关,最主要的是考虑是如何充分利用机器的各
个硬件寄存器存放的有关变量的值 , 以减少对于内存的访问次数 。 另外 , 如何根据机器硬件
执行指令的特点 ( 如流水线、 RISC 、 CISC 、 VLIW 等 ) 而对指令进行一些调整使目标代码比
较短,执行的效率比较高,也是一个重要的研究课题。
2 、汇编
汇编实际上指把汇编语言代码翻译成目标机器指令的过程 。 对于被翻译系统处理的每一个 C
语言源程序 , 都将最终经过这一处理而得到相应的目标文件 。 目标文件中所存放的也就是与
源程序等效的目标的机器语言代码 。 目标文件由段组成 。 通常一个目标文件中至少有两个段 :