C 预处理器
预计时间: 7分钟
在C源代码中插入传给编译器的指令,这些指令称为预处理器指令,预处理器指令并不是C语言的组成部分,但它们扩充了C程序设计环境。
C 预处理器
简单来说,C 预处理器只是一个文本替换工具,它指示编译器在实际编译之前进行所需的预处理。
所有预处理器指令都以井号 (#) 开头,且每条预处理器指令独占一行。
常见的C预处理器应处理指令如下
指令 | 说明 |
---|---|
#include | 文件包含 |
#define | 宏替换 |
#undef | 取消宏替换 |
#ifdef | 测试某个名字是否定义,定义了返回真 |
#ifndef | 测试某个名字是否定义,没定义返回真 |
#if | 测试条件是否为真 |
#else | #if 的替代方案 |
#elif | #if 的另一个测试条件 |
#endif | #if 的结束条件 |
#error | 在标准错误输出上打印错误消息。 |
#pragma | 使用标准化方法向编译器发出特殊命令。 |
#line | 改变__LINE__ 和__FILE__ 的内容。 |
预处理器示例
分析以下示例以了解各种指令。
#define MAX_ARRAY_LENGTH 20
该指令告诉预处理器用 20 替换 MAX_ARRAY_LENGTH 的实例。使用*#define*常量来增加可读性。
#include <stdio.h>
#include "myheader.h"
这些指令告诉预处理器从系统库中获取 stdio.h并将文本添加到当前源文件。下一行告诉预处理器从本地目录获取myheader.h并将内容添加到当前源文件。
#undef FILE_SIZE
#define FILE_SIZE 42
它告诉预处理器取消现有的 FILE_SIZE 定义并将其重新定义为 42。
#ifndef MESSAGE
#define MESSAGE "You wish!"
#endif
它告诉预处理器仅当 MESSAGE 尚未定义时才定义 MESSAGE。
#ifdef DEBUG
/* Your debugging statements here */
#endif
如果定义了 DEBUG,它会告诉预处理器处理随附的语句。
如果在编译时将-DDEBUG
标志传递给 gcc 编译器,这将很有用。这将定义 DEBUG,因此您可以在编译期间即时打开和关闭调试。
预定义宏
ANSI C 定义了许多宏。
宏 | 描述 |
---|---|
__DATE__ | 当前日期为“MMM DD YYYY”格式的字符文字。 |
__TIME__ | 当前时间为“HH:MM:SS”格式的字符文字。 |
__FILE__ | 这包含作为字符串文字的当前文件名。 |
__LINE__ | 这包含作为十进制常量的当前行号。 |
__STDC__ | 当编译器符合 ANSI 标准时定义为 1。 |
让我们试试下面的例子 -
#include <stdio.h>
int main() {
printf("File :%s\n", __FILE__ );
printf("Date :%s\n", __DATE__ );
printf("Time :%s\n", __TIME__ );
printf("Line :%d\n", __LINE__ );
printf("ANSI :%d\n", __STDC__ );
}
当文件test.c中的上述代码被编译并执行时,它会产生以下结果 -
File :test.c
Date :Jun 2 2012
Time :03:36:24
Line :8
ANSI :1
预处理器运算符
C 预处理器提供以下运算符来帮助创建宏 -
宏继续 (\) 运算符
宏通常仅限于一行。宏继续运算符 (\) 用于继续一个对于单行来说太长的宏。例如 -
#define message_for(a, b) \
printf(#a " and " #b ": We love you!\n")
字符串化 (#) 运算符
字符串化或数字符号运算符 ( '#' ) 在宏定义中使用时,会将宏参数转换为字符串常量。此运算符只能用于具有指定参数或参数列表的宏中。例如 -
#include <stdio.h>
#define message_for(a, b) \
printf(#a " and " #b ": We love you!\n")
int main(void) {
message_for(Carole, Debra);
return 0;
}
编译并执行上述代码时,会产生以下结果 -
Carole and Debra: We love you!
标记粘贴 (##) 运算符
宏定义中的标记粘贴运算符 (##) 结合了两个参数。它允许将宏定义中的两个单独的标记连接成一个标记。例如 -
#include <stdio.h>
#define tokenpaster(n) printf ("token" #n " = %d", token##n)
int main(void) {
int token34 = 40;
tokenpaster(34);
return 0;
}
编译并执行上述代码时,会产生以下结果 -
token34 = 40
之所以发生这种情况,是因为此示例导致预处理器的以下实际输出 -
printf ("token34 = %d", token34);
这个例子展示了 token##n 到 token34 的连接,这里我们同时使用了字符串化和标记粘贴。
Defined() 运算符
defined
运算符用于常量表达式,以确定是否使用#define 定义了标识符。如果定义了指定的标识符,则值为真(非零)。如果未定义符号,则值为假(零)。定义的运算符指定如下 -
#include <stdio.h>
#if !defined (MESSAGE)
#define MESSAGE "You wish!"
#endif
int main(void) {
printf("Here is the message: %s\n", MESSAGE);
return 0;
}
编译并执行上述代码时,会产生以下结果 -
Here is the message: You wish!
参数化宏
预处理器的强大功能之一是能够使用参数化宏来模拟功能。例如,我们可能有一些代码来平方一个数字,如下所示 -
int square(int x) {
return x * x;
}
我们可以使用宏重写上面的代码,如下所示 -
#define square(x) ((x) * (x))
带参数的宏必须先使用**#define**指令定义,然后才能使用。参数列表括在括号中,并且必须紧跟在宏名称之后。宏名称和左括号之间不允许有空格。例如 -
#include <stdio.h>
#define MAX(x,y) ((x) > (y) ? (x) : (y))
int main(void) {
printf("Max between 20 and 10 is %d\n", MAX(10, 20));
return 0;
}
编译并执行上述代码时,会产生以下结果 -
Max between 20 and 10 is 20
更新于2022年04月13日