1. 预处理的步骤

现在我们全面了解一下C编译器做语法解析之前的预处理步骤:

1、把第 2 节 “常量”提到过的三连符替换成相应的单字符。

2、把用\字符续行的多行代码接成一行。例如:

  1. #define STR "hello, "\
  2. "world"

经过这个预处理步骤之后接成一行:

  1. #define STR "hello, " "world"

这种续行的写法要求\后面紧跟换行,中间不能有其它空白字符。

3、把注释(不管是单行注释还是多行注释)都替换成一个空格。

4、经过以上两步之后去掉了一些换行,有的换行在续行过程中去掉了,有的换行在多行注释之中,也随着注释一起去掉了,剩下的代码行称为逻辑代码行。然后预处理器把逻辑代码行划分成Token和空白字符,这时的Token称为预处理Token,包括标识符、整数常量、浮点数常量、字符常量、字符串、运算符和其它符号。继续上面的例子,两个源代码行被接成一个逻辑代码行,然后这个逻辑代码行被划分成Token和空白字符:#define,空格,STR,空格,"hello, ",Tab,Tab,"world"

5、在Token中识别出预处理指示,做相应的预处理动作,如果遇到#include预处理指示,则把相应的源文件包含进来,并对源文件做以上1-4步预处理。如果遇到宏定义则做宏展开。

我们早在第 2 节 “数组应用实例:统计随机数”就认识了预处理指示这个概念,现在给出它的严格定义。一条预处理指示由一个逻辑代码行组成,以#开头,后面跟若干个预处理Token,在预处理指示中允许使用的空白字符只有空格和Tab。

6、找出字符常量或字符串中的转义序列,用相应的字节来替换它,比如把\n替换成字节0x0a。

7、把相邻的字符串连接起来。继续上面的例子,如果代码中有:

  1. printf(
  2. STR);

经过第4步处理划分成以下Token:printf(,换行,Tab,STR);,换行。经过第5步宏展开后变成以下Token:printf(,换行,Tab,"hello, ",Tab,Tab,"world");,换行。然后把相邻的字符串连接起来,变成以下Token:printf(,换行,Tab,"hello, world");,换行。

8、经过以上处理之后,把空白字符丢掉,把Token交给C编译器做语法解析,这时就不再是预处理Token,而称为C Token了。这里丢掉的空白字符包括空格、换行、水平Tab、垂直Tab、分页符。继续上面的例子,最后交给C编译器做语法解析的Token是:printf("hello, world");。注意,把一个预处理指示写成多行要用\续行,因为根据定义,一条预处理指示只能由一个逻辑代码行组成,而把C代码写成多行则不需要用\续行,因为换行在C代码中只不过是一种空白字符,在做语法解析时所有空白字符都已经丢掉了。