预处理器
预处理是C编译器做的第一件事情,主要是做一些文本方面的工作。包括:删除注释、插入被#include包含的文件、定义和替换由#define指令定义的符号以及代码的部份内容,和条件编译。
预定义符号
预定义符号如下表所示:
__FILE__ | 进行编译的文件名 |
__LINE__ | 文件的当前行号 |
__DATE__ | 文件编译的日期 |
__TIME__ | 文件编译的时间 |
__STDC__ | 如果遵循ANSI C,它被定义 |
预定义符号都是双下划线,通常用到最多的是前2个,用于打印log.
#define
此指令应该是最为常见的,它的一般描述是
#define name stuff
stuff是可选的。stuff不仅可以是常量,任何文本都可以用于替换。
#define name(parameter-list) stuff
上面这种形式被称为宏macro,它作为函数的替换的方式之一被广泛采用。不过C++中不太推荐这种方法,转而使用模板。不过,宏函数还是有很特殊的地方,且模版亦不能代替。比如字符串代替。
#define PRINT(FORMAT, VALUE) \
printf("The value is "FORMAT" ", VALUE)
在宏参数两边加上引号,表示以字符串形式替换。例如,下面的语句:
PRINT("%d", x+10);
经过预编译后变为
printf("The value is %d", x+10);
这种转换只有当宏参数是字符串常量时,才可以被处理。如果在stuff里宏参数前加上'#',则对任意文本都有效。比如下面
#define PRINT(FORMAT, VALUE) \
printf("The value is "#VALUE" = "FORMAT" ", VALUE)
那么上面那条语句会编译为
printf("The value is x+10 = %d", x+10);
这样大提高了宏参数的灵活性。甚至,预处理器还有连接功能'##'。
#define ADD(N, VALUE) hd##N += VALUE
…
ADD(a, 3); ó had += 3;
在许多的高级程序中,这类“奇技淫巧”被广泛使用。一方面,宏替换比函数往往能获得更高的效率;更重要的是,这类字符串处理是C/C++这种非脚本语言无法实现的。
#undef
通常是重定义一个宏。因为如果一个宏已经定义了,那么必须先取消定义,才能重新定义。
命令行定义
命令行定义就是通过编译器来定义。各种编译器都提供了一个选项来定义宏。以gcc为例:
gcc -DMAX_SIZE=100 -DLITTLE_ENDIAN
这里定义了两个,MAX_SIZE为100,LITTLE_ENDIAN没有指定值,那么默认为0.
命令行也可以取消定义,用-U。方法就不叙述了。(注意:gcc和cl都是区分大小写的)
条件编译
具体地讲,条件编译就是几个条件指令:#if, #ifdef, #elseif, #else, #endif, #ifundef, defined。比如,判断一个宏是否定义,有三种方式:
1. #if MACRO_
2. #ifdef MACRO_
3. #if defined(MACRO_)
一般情况下,用前两个就可以了。但是后一个也非常有用,比如要判断多个条件组合时
#if defined(!MACRO1 || MACRO2 && MACRO3 )
用其它方式会很麻烦。
其它指令
#error text-to-print
让编译器输出信息对开发者来说,比注释更有效。