预处理的步骤
1、三连符替换成相应的单字符(例如用??=表示#字符。)。
2、把用\字符续行的多行代码接成一行。例如:
#define STR "hello, "\
"world"
经过这个预处理步骤之后接成一行:
#define STR "hello, " "world"
这种续行的写法要求\后面紧跟换行,中间不能有其它空白字符。
3、把注释(不管是单行注释还是多行注释)都替换成一个空格。
4、经过以上两步之后去掉了一些换行,有的换行在续行过程中去掉了,有的换行在多行注释之中,也随着注释一起去掉了,剩下的代码行称为逻辑代码行。然后预处理器把逻辑代码行划分成Token和空白字符,这时的Token称为预处理Token,包括标识符、整数常量、浮点数常量、字符常量、字符串、运算符和其它符号。继续上面的例子,两个源代码行被接成一个逻辑代码行,然后这个逻辑代码行被划分成Token和空白字符: #, define,空格, STR,空格, "hello,", Tab, Tab, "world"。
5、在Token中识别出预处理指示,做相应的预处理动作,如果遇到#include预处理指示,则把相应的源文件包含进来,并对源文件做以上1-4步预处理。如果遇到宏定义则做宏展开。一条预处理指示由一个逻辑代码行组成,以#开头,后面跟若干个预处理Token,在预处理指示中允许使用的空白字符只有空格和Tab。
6、找出字符常量或字符串中的转义序列,用相应的字节来替换它,比如把\n替换成字节0x0a。
7、把相邻的字符串连接起来。继续上面的例子,如果代码中有:
printf(
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代码中只不过是一种空白字符,在做语法解析时所有空白字符都已经丢掉了。