一、预处理指令在编译阶段就处理了,所以编译成才后运行的这个阶段已经是预处理后的结果,例如指令
#define BUFFSIZE 4096
的预处理方式是把代码出现有BUFFSIZE的地方都替换为4096;
下面的例子,test会正确打印BUFFSIZE,跟作用域无关:
#include <stdio.h> void test(void); int main(void) { test(); #define BUFFSIZE 4096 } void test(void){ printf("%d ", BUFFSIZE);//输出4096 }
反而跟编译顺序有关,把BUFFSIZE定义放在定义test的行数之下,编译器就会报错:
#include <stdio.h> void test(void); void test(void){ printf("%d ", BUFFSIZE); } int main(void) { test(); #define BUFFSIZE 4096 }
报错:
c72.c: In function ¡®test¡¯: c72.c:7:17: error: ¡®BUFFSIZE¡¯ undeclared (first use in this function) printf("%d ", BUFFSIZE); ^ c72.c:7:17: note: each undeclared identifier is reported only once for each function it appears in
二、遇到带参数的宏,当参数也是宏的时候,作为参数的宏会优先展开。
#include <stdio.h> #define ONE_CHAR 'a' void main(void) { putchar(ONE_CHAR); }
示例中putchar也是宏,它声明在stdio.h里,形式类似于这样
#define putchar(x) putc(x, stdout)
有一点需要注意的是,预处理器只会将宏当作字符串展开,并不会给变量做其他操作(除非使用了字符串化运算符操作,参考《C语言核心技术》第14章:字符串化运算),所以宏ONE_CHAR的代替字符串中必须包含单引号,展开后代码就恰好给putchar传入了一个字符常量。
去掉引号的代码会引起报错:
#include <stdio.h> #define ONE_CHAR a void main(void) { putchar(ONE_CHAR); }
c81.c: In function ¡®main¡¯: c81.c:3:18: error: ¡®a¡¯ undeclared (first use in this function) #define ONE_CHAR a ^
原因是展开宏后是这样的:
putc(a, stdout);
表达式中,a被视为标识符,因为编译器找不到标识符a的声明,所以抛出以上报错信息。
声明一个a自变量,就没问题了:
#include <stdio.h> #define ONE_CHAR a void main(void) { char a = 'a'; putchar(ONE_CHAR);//打印出a }
三、可选性自变量的宏
在C99标准下,可以定义有省略符号(...)的宏,省略符号必须放在最后面,表示“可选的”。
当调用时,预处理器会将“所有可选性自变量”,连同分隔它们的逗号,聚集在一起当作一个自变量。在代替文字当中,把__VA_ARGS__这个标识符替换成这个自变量。
宏调用的时候,必须有“至少一个”参数。
例如,定义一个打印日志到文件的宏:
#include <stdio.h> #define printLog(...) fprintf(fp, __VA_ARGS__) void main(void) { FILE* fp = fopen("./c81.txt", "a"); int intVar = 5; printLog("%s: intVar = %d ", __func__, intVar); }
printLog宏展开后,类似于这种形式:
fprintf(fp, "%s: intVar = %d ", __func__, intVar);
四、字符串化运算符:#
在宏的代替字符内容中的参数前面增加#,会把一个这个参数转换成字符串。
#include <stdio.h> #include <math.h> #define printDBL(exp) printf("express:"#exp " = %f ", exp) void main(void) { printDBL( 4 * atan(1.0) ); }
输出:
express:4 * atan(1.0 ) = 3.141593
预处理器会把对应的自变量放在一对双引号中,形成一个字符串字面值。自变量中的所有字符本身维持不变,但是下面是例外:
- 在记号之间,如果有任何空白(whitespace)字符序列,则会被替换成一个空格(space)字符。
printDBL( 4 * atan(1.0 ) );//就算把以上例子改为这样,输出也是一样的,因为所有连续不连续的空白字符都转换成一个空格了
- 自变量中每个双引号(")的前面会放置一个反斜杠(backslash)。
- 自变量中每个反斜杠的前面放置一个反斜杠。但如果此反斜杠是通用字符名称的一部分,则不会在前面放置一个反斜杠(见《C语言核心技术》第1章:通用字符名称)。