【C语言】预处理器
文章目录
1.简介
C 语言编译器在编译程序之前,会先使用预处理器(preprocessor)处理代码。
预处理器首先会清理代码,进行删除注释、多行语句合成一个逻辑行等工作。然后,执行#开头的预处理指令。
预处理指令可以出现在程序的任何地方,但是习惯上,往往放在代码的开头部分。
每个预处理指令都以#开头,放在一行的行首,指令前面可以有空白字符(比如空格或制表符)。#和指令的其余部分之间也可以有空格,但是为了兼容老的编译器,一般不留空格。
所有预处理指令都是一行的,除非在行尾使用反斜杠,将其折行。指令结尾处不需要分号。
2.#define宏定义
2.1基本用法
1.#define
是最常见的预处理指令,用来将指定的词替换成另一个词。它的参数分成两个部分,第一个参数就是要被替换的部分,其余参数是替换后的内容。每条替换规则,称为一个宏(macro)。
#define MAX 100
上面示例中,
#define
指定将源码里面的MAX
,全部替换成100
。MAX
就称为一个宏。宏的名称不允许有空格,而且必须遵守 C 语言的变量命名规则,只能使用字母、数字与下划线(
_
),且首字符不能是数字。
2.宏是原样替换,指定什么内容,就一模一样替换成什么内容。
#define HELLO "Hello, world"
// 相当于 printf("%s", "Hello, world");
printf("%s", HELLO);
上面示例中,宏
HELLO
会被原样替换成"Hello, world"
。
#define
指令可以出现在源码文件的任何地方,从指令出现的地方到文件末尾都有效。习惯上,会将#define
放在源码文件的头部。它的主要好处是,会使得程序的可读性更好,也更容易修改。
3.#define
指令从#
开始,一直到换行符为止。如果整条指令过长,可以在折行处使用反斜杠,延续到下一行。
#define OW "C programming language is invented \
in 1970s."
上面示例中,第一行结尾的反斜杠将
#define
指令拆成两行。
4.#define
允许多重替换,即一个宏可以包含另一个宏。
#define TWO 2
#define FOUR TWO*TWO
上面示例中,
FOUR
会被替换成2*2
。
5.注意,如果宏出现在字符串里面(即出现在双引号中),或者是其他标识符的一部分,就会失效,并不会发生替换。
#define TWO 2
// 输出 TWO
printf("TWO\n");
// 输出 22
const TWOs = 22;
printf("%d\n", TWOs);
上面示例中,双引号里面的
TWO
,以及标识符TWOs
,都不会被替换。
6.同名的宏可以重复定义,只要定义是相同的,就没有问题。如果定义不同,就会报错。
// 正确
#define FOO hello
#define FOO hello
// 报错
#define BAR hello
#define BAR world
上面示例中,宏
FOO
没有变化,所以可以重复定义,宏BAR
发生了变化,就报错了。
2.2带参数的宏
基本用法
1.多个参数
宏的强大之处在于,它的名称后面可以使用括号,指定接受一个或多个参数。
#define SQUARE(X) X*X
上面示例中,宏SQUARE
可以接受一个参数X
,替换成X*X
。
注意,宏的名称与左边圆括号之间,不能有空格。
这个宏的用法如下。
// 替换成 z = 2*2;
z = SQUARE(2);
注意有坑!!
这种写法很像函数,但又不是函数,而是完全原样的替换,会跟函数有不一样的行为。
#define SQUARE(X) X*X // 输出19 printf("%d\n", SQUARE(3 + 4));
上面示例中,
SQUARE(3 + 4)
如果是函数,输出的应该是49(7*7
);宏是原样替换,所以替换成3 + 4*3 + 4
,最后输出19。可以看到,原样替换可能导致意料之外的行为。
**解决办法:**就是在定义宏的时候,尽量多使用圆括号,这样可以避免很多意外。
#define SQUARE(X) ((X) * (X))
上面示例中,
SQUARE(X)
替换后的形式,有两层圆括号,就可以避免很多错误的发生。
2.空参数
宏的参数也可以是空的。
#