深入了解预处理指令
预处理指令是以#号开头的代码行。#号必须是该行除了任何空白字符外的第一个字符。#后是指令关键字,在关键字和#号之间允许存在任意个数的空白字符。整行语句构成了一条预处理指令,该指令将在编译器进行编译之前对源代码做某些转换。
指令用途:
#空指令,无任何效果
#include包含一个源代码文件
#define定义宏
#undef取消已定义的宏
#if 如果给定条件为真,则编译下面代码
#ifdef如果宏已经定义,则编译下面代码
#ifndef如果宏没有定义,则编译下面代码
#elif如果前面的#if给定条件不为真,当前条件为真,则编译下面代码
#endif结束一个#if……#else条件编译块
#error停止编译并显示错误信息
文件包含
#include
预处理指令的作用是在指令处展开被包含的文件。包含可以是多重的,也就是说一个被包含的文件中还可以包含其他文件。标准 C 编译器至少支持八重嵌套包含。
宏
宏定义了一个代表特定内容的标识符。预处理过程会把源代码中出现的宏标识符替换成宏定义时的值。宏最常见的用法是定义代表某个值的全局符号。宏的第二种用法是定义带参数的宏,这样的宏可以象函数一样被调用,但它是在调用语句处展开宏,并用调用时的实际参数来代替定义中的形式参数。
#运算符
出现在宏定义中的#运算符把跟在其后的参数转换成一个字符串。有时把这种用法的#称为字符串化运算符。例如:
#definePASTE(n)"adhfkj"#n
main()
{
printf("%s ",PASTE(15));
}
宏定义中的#运算符告诉预处理程序,把源代码中任何传递给该宏的参数转换成一个字符串。所以输出应该是 adhfkj15。
##运算符
用于把参数连接到一起。预处理程序把出现在##两侧的参数合并成一个符号。看下面的例子:
#defineNUM(a,b,c)a##b##c
#defineSTR(a,b,c)a##b##c
main()
{
printf("%d ",NUM(1,2,3));
printf("%s ",STR("aa","bb","cc"));
}
最后程序的输出为:
123
aabbcc1
条件编译指令
条件编译指令将决定那些代码被编译,而哪些是不被编译的。可以根据表达式的值或者某个特定的宏是否被定义来确定编译条件。
#if指令
检测跟在制造另关键字后的常量表达式。如果表达式为真,则编译后面的代码,知道出现#else、#elif 或#endif 为止;否则就不编译。
#endif指令
用于终止#if 预处理指令。
#ifdef和#ifndef
用于#define 命令定义过则进行编译
#else指令
用于某个#if 指令之后,当前面的#if 指令的条件不为真时,就编译#else 后面的代码。#endif 指令将中指上面的条件块。
#elif指令
综合了#else 和#if 指令的作用。
其他一些标准指令
#error
指令将使编译器显示一条错误信息,然后停止编译。
#line
指令可以改变编译器用来指出警告和错误信息的文件号和行号。
#pragma
指令没有正式的定义。编译器可以自定义其用途。典型的用法是禁止或允许某些烦人的警告信息。
补充:
预处理就是在进行编译的第一遍词法扫描和语法分析之前所作的工作。说白了,就是对源文件进行编译前,先对预处理部分进行处理,然后对处理后的代码进行编译。这样做的好处是,经过处理后的代码,将会变的很精短。
情况 1
#ifdef _XXXX
...程序段1...
#else
...程序段2...
#endif
这表明如果标识符_XXXX 已被#define 命令定义过则对程序段 1 进行编译;否则对程序段 2 进行编译。
情况 2
#ifndef _XXXX
...程序段1...
#else
...程序段2...
#endif
这里使用了#ifndef,表示的是 if not def。当然是和#ifdef 相反的状况(如果没有定义了标识符_XXXX,那么执行程序段 1,否则执行程序段 2)。例子就不举了。
情况 3
#if 常量
...程序段1...
#else
...程序段2...
#endif
这里表示,如果常量为真(非 0,随便什么数字,只要不是 0),就执行程序段 1,否则执行程序段 2。
我认为,这种方法可以将测试代码加进来。当需要开启测试的时候,只要将常量变 1 就好了。而不要测试的时候,只要将常量变 0。