C语言的预处理过程

来源:互联网 发布:费米估算法训练 编辑:程序博客网 时间:2024/06/17 05:14

最近在学习C语言的相关内容,其中关于预处理过程过程感觉对自己帮助较大,所以写出来与大家分享,有不对的地方还请指正,欢迎留言讨论。


C语言预处理过程如下:

  1. 替换源文本中的三联符。三连符是C语言为了适应某些输入设备上没有的字符而设定的,比如??=表示"#"。
  2. 去掉文件中的续行符以及紧邻的换行符,将两个物理行组成一个逻辑行。
  3. 将源文本中的注释替换成一个空格。
  4. 将源文本拆成预处理token和空白字符。即将源文本拆成一个个处理单元,包括头文件名、标识符(包括关键字)、预处理数字、字符常量、字符串字面值(string-literal)、符号等。
  5. 执行文件预处理指令,对宏进行展开,如果遇到include指令,则会对include的文件进行1到5的步骤。
  6. 将源文本中的转义序列替换成对应的编码。
  7. 合并两个相邻的字符串。
  8. 去掉空白字符,将剩余的token(这个时候称为C token)传给编译器进行编译。
在上述过程中尤其要注意以下几点。
  1. 第四点中提到的预处理token比较重要,它是宏展开的处理单位。
  2. 要区分字符串字面值和头文件名,即使头文件的引入使用的是双引号,也不能认为它是字符串字面值,在头文件中的转义字符是不起作用的,因为它不是字符串字面值。
  3. 第五点执行了宏的展开操作。

宏展开过程

下面重点讲下宏的处理过程。
在宏处理过程中,有两个运算符,一个是#操作符,一个是##操作符。
#操作符:只能用在宏函数中,后面跟形参,主要作用是将形参所代表的内容转化成字符串,对原形参代表内容中的字符串内容进行转义处理。
如:
#define fun(a) #a;
fun( a b c d \n "string\n" e f g);
则宏展开后的结果为“a b c d \n \"string\\n\" e f g”,其中要特别注意转化成字符串的时候,对原有内容中的字符串内容的转义字符和引号的处理,即原来字符串的转义字符也被再次转义,由 \n 变成了 \\n ,字符串界定符 " 变成了 \"。在这个过程中应该注意的是,非字符串中的 \n 并没有被转义。
##操作符:被用于函数式宏的定义和非函数式宏的定义,它把该运算符前后两个预处理token连成一个预处理token。
如:
define HASH_HASH # ## #
HASH_HASH 
则宏展开后的结果为##,注意,这个时候,它不是运算符,它是前后两个token合并后的结果。

非函数式宏展开比较简单,在此不进行叙述。
函数是宏展开过程:如果函数宏中参数的前一个token是运算符#或者前后的token有一个是运算符##,则该参数不进行展开,而是直接进行相应的运算,如果参数没有满足前面的情况,则先对参数做充分展开,然后在进行替换,当宏展开完成以后,展开结果会被重新扫描以确定是否有新的宏调用产生(比如通过##运算产生),如果有则继续展开;在继续展开的过程中,有一个递归个问题,即如果一个宏的标识是由本身进一步展开获得的,则该宏不再展开,具体见下文第二个例子。

举个例子:
一:
#define sh(x) printf("n" #x "=%d,or %d\n",n##x,alt[x])
#define sub_z 26
sh(sub_z)
宏展开以后结果为:printf("n" "sub_z" "=%d,or %d\n",nsub_z,alt[26])
在这个过程中,第一个x因为在#后面,所以没有展开,第二个x在##后面,所以也没有展开,第三个x则需要完全展开以后才能替换。

二:
#define func(x) func(x)+funa(x)
#define funa(x) ab+x
#define ab a+b
func(10)
则宏展开结果为func(10)+a+b+10
这个过程中,func(10)中的func本身是由func宏展开的,因此不能在利用func宏再次展开。

如果想查看预处理的处理结果,可以使用“gcc -E 文件 ”进行编译,输出为预处理结果。

本博客发得比较仓促,如有问题欢迎指正和讨论,谢谢!

原创粉丝点击