《C Primer Plus》16:C预处理器和C库部分知识点整合

《C Primer Plus》-16章:C预处理器和C库

🐷复制的C Primer Plus

16.2 #define(符号常量)

预处理器指令从#开始运行,到后面的第1个换行符为止。预处理器不做计算,不对表达式求值,它只进行替换。

C语言现在也支持const关键字,提供了更灵活的方法。用const可以创建在程序运行过程中不能改变的变量,可具有文件作用域或块作用域。另一方面,宏常量可用于指定标准数组的大小和const变量的初始值。举例说明:

#define LIMIT 20 
const int LIM = 50;
static int data1[LIMIT]; // 有效
static int data2[LIM]; // 无效

因为const关键字只在程序运行(编译)过程中创建不能改变的量,但是宏常量却在编译之前的预编译步骤就展开了,所以可以作为非自动数组的大小。

16.2.1 记号

(感觉暂时用不上)

从技术角度来看,可以把宏的替换体看作是记号(token)型字符串, 而不是字符型字符串。例如:

#define EIGHT 4 * 8

如果预处理器把该替换体解释为字符型字符串,将用4 * 8替换EIGHT。 即,额外的空格是替换体的一部分。如果预处理器把该替换体解释为记号型 字符串,则用3个的记号4 * 8(分别由单个空格分隔)来替换EIGHT。换而言之,解释为字符型字符串,把空格视为替换体的一部分;解释为记号型字 符串,把空格视为替换体中各记号的分隔符。这个看具体的C编译器怎么处理。

16.3 在#define中使用参数(类函数宏)

在#define中使用参数可以创建外形和作用与函数类似的类函数宏。

在这里插入图片描述

/* mac_arg.c -- 带参数的宏 */
#include <stdio.h>
#define SQUARE(X) X*X
#define PR(X) printf("The result is %d.\n", X)
int main(void)
{
	int x = 5;
	int z;
	printf("x = %d\n", x);
	z = SQUARE(x);
	printf("Evaluating SQUARE(x): ");
	PR(z);
    
	z = SQUARE(2);/*1.预处理:z = 2*2;  2.编译:z = 4;*/
	printf("Evaluating SQUARE(2): ");
	PR(z);			//The result is 2
    
	printf("Evaluating SQUARE(x+2): ");
	PR(SQUARE(x + 2));  //1.预处理:The result is x+2*x+2   2.编译: The result is 17
    
	printf("Evaluating 100/SQUARE(2): ");
	PR(100 / SQUARE(2)); //1.预处理:The result is 100/2*2  2.编译: The result is 100
    
	printf("x is %d.\n", x);
	printf("Evaluating SQUARE(++x): ");
	PR(SQUARE(++x));   //1.预处理:The result is ++x*++x   2.编译: The result is 6*7
    //SQUARE(++x)变成了++x*++x,递增了两次x,一次在乘法运算之前,一次在乘法运算之后:
    
	printf("After incrementing, x is %x.\n", x);
	return 0;
}

这里,SQUARE 是宏标识符,SQUARE(X)中的 X 是宏参数,X * X 是替换列表。宏定义中的 X由宏调用中的符号代替。因此,SQUARE(2)替换为2*2,X实际上起到参数的作用。

C语言中a++和++a的区别是:二者的运算过程不同,a++表示先使用a的值,然后再对a做加1处理;++a表示先对a做加1处理,然后再使用a的值。a++和++a都等价于a = a+1

SQUARE(++x)变成了++x*++x,递增了两次x,一次在乘法运算之前,一次在乘法运算之后。一般而言,避免在宏中使用递增或递减运算符。但是,++x可作为函数参数,因为编译器 会对++x求值得5后,再把5传递给函数。

16.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"的形参名。这个过程称为字符串化 。

例如下面的代码:调用第1个宏时,用"y"替换#x。调用第2个宏时,用"2 + 4"替换#x。

/* subst.c -- 在字符串中替换 */
#include <stdio.h>
#define PSQR(x) printf("The square of " #x " is %d.\n",((x)*(x)))
int main(void)
{
	int y = 5;
	PSQR(y);   				//The square of y is 25
	PSQR(2 + 4);  			//The square of 2 + 4 is 36
	return 0;
}

16.3.2 ##运算符:组合替换体(记号)

##运算符把两个记号组合成一个记号(两个替换体–>一个替换体)。例如,可以这样做:

#define XNAME(n) x ## n 

然后,宏XNAME(4)将展开为x4

16.3.3 变参宏:…和_ _ VA_ARGS_ _

通过把宏参数列表中最后的参数写成省略号(即,3个点…)来实现变参宏的功能。这样,预定义宏 _ VA_ARGS _可用在替换部分中,表明省略号代表什么。

例如下面的定义:

#define PR(...) printf(_ _VA_ARGS_ _)

若调用宏:

PR("Howdy");
PR("weight = %d, shipping = $%.2f\n", wt, sp);

对于第1次调用,__ VA_ARGS _ _ 展开为1个参数:“Howdy”。 对于第2次调用,_ VA_ARGS _展开为3个参数:“weight = %d, shipping = $%.2f\n”、wt、sp。

因此,展开后的代码为:

printf("Howdy");
printf("weight = %d, shipping = $%.2f\n", wt, sp);

16.4 宏和函数的选择

宏和函数的选择实际上是时间和空间的权衡。

宏生成内联代码,即在程序中生成语句。如果调用20次宏,即在程序中插入20行代码。

如果调用函数 20次,程序中只有一份函数语句的副本,所以节省了空间。然而另一方面, 程序的控制必须跳转至函数内,随后再返回主调程序,这显然比内联代码花费更多的时间。

16.5 文件包含:#include

#include命令:把被包含文件的全部内容输入到源文件#include指令所在的位置

#include <stdio.h> //←查找系统目录
#include "hot.h"   //←查找当前工作目录

为什么要包含文件?因为编译器需要这些文件中的信息。例如,stdio.h 文件中通常包含EOF、NULL、getchar()和 putchar()的定义。getchar()和 putchar()被定义为宏函数。此外,该文件中还包含C的其他I/O函数。

头文件中常见的内容:#define指令、结构声明、 typedef和函数原型。注意,这些内容是编译器在创建可执行代码时所需的信 息,而不是可执行代码。

可执行代码通常在源代码文件中,而不是在头文件中。

16.6 其他指令

16.6.1 #undef指令

如果想使用一个名称,又不确定之前是否已经用过,为安全起见,可以用#undef 指令取消该名字的定义。#undef指令用于“取消”已定义的#define指令。

16.6.3 条件编译

  1. #ifdef、#else和#endif指令
#ifdef MAVIS
#include "horse.h"// 如果已经用#define定义了 MAVIS,则执行下面的指令
#define STABLES 5

#else
#include "cow.h" //如果没有用#define定义 MAVIS,则执行下面的指令
#define STABLES 15
#endif
  1. #ifndef指令

#ifndef指令与#ifdef指令的用法类似,也可以和#else、#endif一起使用, 但是它们的逻辑相反。

  1. .#if和#elif指令

#if指令很像C语言中的if。#if后面跟整型常量表达式,如果表达式为非零,则表达式为真。可以在指令中使用C的关系运算符和逻辑运算符:

#if SYS == 1
#include "ibm.h"
#endif
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值