C预处理器和C库

一、翻译程序的第一步

在预处理之前,编译器必须对该程序进行一些翻译处理。首先,编译器把源代码中出现的字符映射到源字符集。该过程处理多字节字符和三字符序列。

第二,编译器定位每个反斜杠后面跟着换行符的实例,并删除它们。也就是说,把下面两个物理行:

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()宏用#运算符组合字符串,##运算符把记号组合为一个新的标识符。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值