一、翻译程序的第一步
在预处理之前,编译器必须对该程序进行一些翻译处理。首先,编译器把源代码中出现的字符映射到源字符集。该过程处理多字节字符和三字符序列。
第二,编译器定位每个反斜杠后面跟着换行符的实例,并删除它们。也就是说,把下面两个物理行:
printf("That's wond\
erful!\n");
转换成一个逻辑行:
printf("That's wonderful!\n");
注意,在这种场合,“换行符”的意思是通过按下Enter键在源代码文件中换行所生成的字符,而不是指符号表征\n.
二、明示常量:#define
#define预处理器指令和其他预处理器指令一样,以#号作为一行的开始。ANSI和后来的标准都允许#号前面有空格和制表符,而且还允许在#和指令的其余部分之间有空格。但是旧版本的C要求指令从一行最左边开始,而且#号和指令其余部分之间不能有空格。指令可以出现在源文件的任何地方,器定义从指令出现的地方到该文件末尾有效。我们大量使用#define指令来定义明示常量(也叫做符号常量),但是该指令还有许多其他用途。程序16.1演示了#define指令的一些用法和属性
/*程序16.1* preproc.c程序*/
#include<stdio.h>
#define TWO 2 //可以使用注释
#define OW "consistency is the last refuge of the unimagina\
tive. -Oscar Wilder" //反斜杠把该定义延续到下一行
#define FOUR TWO*TWO
#define PX printf("X is %d. \n",x)
#define FMT "X is %d.\n"
int main(void){
int x=TWO;
PX;
x=FOUR;
printf(FMT,x);
printf("%s\n",OW);
printf("TWO: OW\n");
return 0;
}
每行#define(逻辑行)都由三部分组成。第一部分是#define指令本身。第二部分是选定的缩写,也称为宏。有些宏代表值(如本例),这些宏被称为类对象宏。C语言还有类函数宏。宏的名称中不允许有空格,而且必须遵循C变量的命名规则:只能使用字符、数字和下划线字符,而且首字母不能是数字。第三部分(指令行的其余部分)称为替换列表或替换体,见图16.1:
一旦预处理器在程序中找到宏的实例后,就会用替换体代替该宏。从宏变成最终替换文本的过程称为宏展开。注意,可以在#define行使用标准C注释。如前所述,每条注释都会被一个空格代替。
运行程序16.1后,输出如下所示:
x is 2.
x is 4.
Consistency is the last refuge of the unimaginative. -Socar Wilde
TWO: OW
宏展开到此为止。由于编译器在编译期间对所有的常量表达式(只包含常量的表达式)求值,所以预处理器不会进行实际的乘法运算,这一过程在编译时进行。预处理器不做计算,不对表达式求值,它只进行替换。
注意,宏定义还可以包含其他宏(一些编译器不支持这种嵌套功能)。
程序的下一行:
2.1 记号
2.2 重定义常量
三、在#define中使用参数
在#define中使用参数可以创建外形和作用与函数类似的类函数宏。带有参数的宏看上去很像函数,因为这样的宏也使用圆括号。类函数宏定义的圆括号中可以有一个或多个参数,随后这些参数出现在替换体中,如图16.2所示:
下面是一个类函数宏的示例:
#define SQUARE(X) X*X
在程序中可以这样用:
z=SQUARE(2);
3.1 用宏参数创建字符串:#运算符
下面是一个类函数宏:
#define PSQR(X) printf("The square of X is %d.\n",((X)*(X)));
假设这样使用宏:
PSQR(8);
输出为:
The square of X is 64.
注意双引号字符串中的X被视为普通文本,而不是一个可被替换的记号。
C允许在字符串中包含宏参数。在类函数宏的替换中,#号作为一个预处理运算符,可以把记号转换成字符串。例如,如果x是一个宏形参,那么#x就是转换为字符串“x”的形参名。这个过程称为字符串化。程序16.3演示了该过程的用法:
3.2 预处理器粘合剂:##运算符
与#运算符类似,##运算符可用于类函数宏的替换部分。而且,##还可用于对象宏的替换部分。##运算符把两个记号组合成一记号。例如,可以这样做:
#define XNAME(n) x##n
然后,宏NAME(4)
将展开为x4,程序16.4演示了##作为记号粘合剂的用法:
/*程序16.4 glue.c*/
#include<stdio.h>
#define XNAME(n) x##n
#define PRINT_XN(n) printf("x" #n " = %d\n,x##n);
int main(void){
int XNAME(1)=14; //int x1=14;
int XNAME(2)=20; //int x2=20;
int x3=30;
PRINT_XN(1); //printf("x1=%d\n",x1);
PRINT_XN(2); //printf("x2=%d\n",x2);
PRINT_XN(3); //printf("x3=%d\n",x3);
return 0;
}
该程序的输出如下:
x1=14
x2=20
x3=30
注意,PRINT_XN()宏用#运算符组合字符串,##运算符把记号组合为一个新的标识符。