众所周知,你的程序编译前要做的事就是扫描源代码,对其做初步的转换,产生新的源代码提供给编译器,这个过程就叫编译预处理。这个处理过程由预处理器来完成,预处理器是在程序真正运行前由编译器调用的预处理程序。
常见的预处理有以下三种:
文件包含:#include 是一种最为常见的预处理,主要是做为文件的引用组合源程序正文。
宏替换: #define,这是最常见的用法,它可以定义符号常量、函数功能、重新命名、字符串的拼接等各种功能。
条件编译:#if,#ifndef,#ifdef,#elif,#endif,#undef等也是比较常见的预处理,主要是进行编译时进行有选择的挑选,注释掉一些指定的代码,以达到版本控制、防止对文件重复包含的功能。
一,文件包含
#include预处理指令的作用是在指令处展开被包含的文件。包含可以是多重的,也就是说一个被包含的文件中还可以包含其他文件。预处理器至多支持15层嵌套包含。
预处理过程不检查在转换单元中是否已经包含了某个文件并阻止对它的多次包含。这样就很容易发生相同的一个文件被包含多次的情况,可以轻易地利用条件式编译的预处理指令避免多次包含相同的文件,例如:
- /*incfile.h*/
- #ifndef INCFILE_H_
- #define INCFILE_H_
- /*...incfile.h实际的内容写在这里...*/
- #endif
文件包含中的<>和" ""的区别就不用说了。
二,宏
可以利用预处理指令#define来定义宏。使用预处理指令可以把一个名称指定成任何文字,例如常量值或者语句。当定义宏后,并且此宏的名称出现在源代码中,预处理器就会把它替换掉。
由于宏替换是简单的替换,所以在定义宏的时候务必要加(),如:
#define MAX(x,y) ((x)>(y))?(x):(y)
在宏中还有两类重要的运算符:字符串化运算符和“粘贴记号”运算符
单元运算符#常常被称为字符串化运算符(stringizing operator),因为它会把一个宏参数转换为字符串。#的操作数必须是“宏替代文字”中的参数arg,当参数名称出现在替代文字中,并且前置#字符时,预处理器会把对应的自变量放在一对引号中,形成一个字符串字面值。如下所示:
- #define printAngle(arg) printf(#arg " = %f",arg)
- int main()
- {
- printAngle(3 * 0.5);
- return 0;
- }
上面程序的输出是:3 *0.5 = 1.500000
##运算符是一个二元运算符,可以出现在任何宏的替代文字中。此运算符会把左和右操作数结合咋i一起,称为一个记号,常常被称为“粘贴记号”运算符。如下所示:
- #define TEXT "hello,world,"
- #define msg(x) puts(TEXT ## x)
- int main()
- {
- msg(" it's cobing");
- return 0;
- }
上面程序的输出是: hello,world, it's cobing
如果不显示地取消宏的定义,宏的作用域是全局的,即当前编译单元内所有的文件都是可见的。可以用#undef macro_name 来取消宏定义
三 条件编译
条件式编译区域始于#if, #ifdef或#ifndef等预处理指令,结束于#endif预处理指令。条件式编译区域内可以有任意数目的#elif预处理指令,以及最多一个#else预处理指令,以#if开始的条件式编译区域具有下面的格式:
#if expression1
[group1 ]
[#elif expression2
[group2 ]]
...
[#elif expression3
[group3 ]]
[#else
[group(n+1) ]]
#endif
预处理器会依序计算条件表达式,直到发现某个结果非0的表达式。预处理器会保留对应group内的源代码,共后续处理。在预处理器结束时,预处理器会删除程序中没有被保留的group。下面是一个实例代码
- #if defined(__unix__) && defined(__gnuc__)
- /*...*/
- #endif
#ifdef和#ifndef预处理指令可以测试是否某个宏被定义
- #ifndef MACRO_A
- #define MACRO_A
- /*...*/
- #endif
#error预处理指令会让预处理器发出错误信息:
#error [text]
如果可选性的text出现,这段文字就会被包含在预处理器中有关该错误的错误信息中。然后编译器会停止处理源代码,并结束施行,仿佛遇到了严重的错误。
- #ifndef LEX
- #error "LEX is not defined"
- #endif
如果LEX未被定义,上面的程序编译不成功,编译器提示"fatal error:LEX is not defined"