C - 预处理器
C 预处理器 不是编译器的一部分,而是编译过程中的一个单独步骤。简单来说,C 预处理器只是一个文本替换工具,它指示编译器在实际编译之前进行所需的预处理。我们将 C 预处理器称为 CPP。
所有预处理器命令都以井号 (#) 开头。它必须是第一个非空白字符,为了便于阅读,预处理器指令应该从第一列开始。以下部分列出了所有重要的预处理器指令 -
Sr.No. | 指令和说明 |
---|---|
1 | #define 替换预处理器宏。 |
2 | #包括 插入来自另一个文件的特定标题。 |
3 | #undef 取消定义预处理器宏。 |
4 | #ifdef 如果定义了此宏,则返回 true。 |
5 | #ifndef 如果未定义此宏,则返回 true。 |
6 | #如果 测试编译时条件是否为真。 |
7 | #else #if 的替代方案。 |
8 | #elif #else 和 #if 在一个语句中。 |
9 | #endif 有条件地结束预处理器。 |
10 | #错误 在 stderr 上打印错误消息。 |
11 | #pragma 使用标准化方法向编译器发出特殊命令。 |
预处理器示例
分析以下示例,了解各种指令。
#define MAX_ARRAY_LENGTH 20
该指令告诉 CPP 将 MAX_ARRAY_LENGTH 的实例替换为 20。使用 #define 用于增加可读性的常量。
#include <stdio.h> #include "myheader.h"
这些指令告诉 CPP 从 System Libraries 获取 stdio.h 并将文本添加到当前源文件。下一行告诉 CPP 获取 myheader.h 从本地目录并将内容添加到当前源文件中。
#undef FILE_SIZE #define FILE_SIZE 42
它告诉 CPP 取消定义现有的 FILE_SIZE 并将其定义为 42。
#ifndef MESSAGE #define MESSAGE "You wish!" #endif
它告诉 CPP 仅当 MESSAGE 尚未定义时才定义 MESSAGE。
#ifdef DEBUG /* Your debugging statements here */ #endif
如果定义了 DEBUG,它会告诉 CPP 处理随附的语句。如果您通过 -DDEBUG,这很有用 在编译时标记到 gcc 编译器。这将定义 DEBUG,因此您可以在编译期间即时打开和关闭调试。
预定义宏
ANSI C 定义了许多宏。虽然每个都可以在编程中使用,但预定义的宏不应该直接修改。
Sr.No. | 宏和说明 |
---|---|
1 | __DATE__ 当前日期为“MMM DD YYYY”格式的字符文字。 |
2 | __TIME__ 当前时间为“HH:MM:SS”格式的字符文字。 |
3 | __FILE__ 这包含作为字符串文字的当前文件名。 |
4 | __LINE__ 这包含作为十进制常量的当前行号。 |
5 | __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")
字符串化 (#) 运算符
stringize 或数字符号运算符 ( '#' ) 在宏定义中使用时,会将宏参数转换为字符串常量。此运算符只能用于具有指定参数或参数列表的宏中。例如 -
现场演示#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 的连接,这里我们使用了 stringize 和令牌粘贴 .
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!
参数化宏
CPP 的强大功能之一是能够使用参数化宏来模拟功能。例如,我们可能有一些代码来计算一个数字,如下所示 -
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
C语言