C 语言中的预处理器
C 预处理器不是编译器的一部分,而是编译过程中的一个独立步骤。简单来说,C 预处理器只是一个文本替换工具,它指示编译器在实际编译之前进行必要的预处理。我们将 C 预处理器称为 CPP。
在 C 语言编程中,预处理是 C 代码编译的第一步。它发生在标记化步骤之前。预处理器的一个重要功能是包含包含程序中使用的库函数的头文件。C 语言中的预处理器还定义常量并扩展宏。
C 语言中的预处理器语句称为指令。程序的预处理器部分始终位于 C 代码的顶部。每个预处理器语句都以井号 (#) 开头。
C 语言中的预处理器指令
下表列出了所有重要的预处理器指令 -
指令 | 描述 |
---|---|
# Define | 替换预处理器宏。 |
# Include | 插入来自另一个文件的特定头文件。 |
# Undef | 取消定义预处理器宏。 |
#ifdef | 如果此宏已定义,则返回 true。 |
#ifndef | 如果此宏未定义,则返回 true。 |
#if | 测试编译时条件是否为真。 |
#else | #if 的替代方案。 |
#elif | #else 和 #if 合二为一语句。 |
#endif | 结束预处理器条件。 |
#error | 在 stderr 上打印错误消息。 |
#pragma | 使用标准化方法向编译器发出特殊命令。 |
预处理器示例
分析以下示例以理解各种指令。
此 #define 指令指示 CPP 将 MAX_ARRAY_LENGTH 的实例替换为 20。使用 #define 常量可提高可读性。
#define MAX_ARRAY_LENGTH 20
以下指令指示 CPP 从系统库中获取"stdio.h"并将文本添加到当前源文件中。下一行指示 CPP 从本地目录中获取"myheader.h"并将内容添加到当前源文件中。
#include <stdio.h> #include "myheader.h"
现在,请看一下以下 #define 和 #undef 指令。它们指示 CPP 取消定义现有的 FILE_SIZE,并将其定义为 42。
#undef FILE_SIZE #define FILE_SIZE 42
以下指令指示 CPP 仅在 MESSAGE 尚未定义时定义 MESSAGE。
#ifndef MESSAGE #define MESSAGE "You wish!" #endif
如果定义了 DEBUG,则以下指令告诉 CPP 处理所附的语句。
#ifdef DEBUG /* Your debugging statements here */ #endif
如果您在编译时将 -DDEBUG 标志传递给 gcc 编译器,这将非常有用。这将定义 DEBUG,以便您可以在编译过程中动态地打开或关闭调试功能。
C 语言中的预定义宏
ANSI C 定义了许多宏。虽然每个宏都可以在编程中使用,但不应直接修改预定义的宏。
宏 | 描述 |
---|---|
__DATE__ | 以字符形式表示的当前日期,格式为"MMM DD YYYY"。 |
__TIME__ | 以字符形式表示的当前时间,格式为"HH:MM:SS"。 |
__FILE__ | 包含当前文件名的字符串字面量。 |
__LINE__ | 包含当前行号的十进制常量。 |
__STDC__ | 当编译器符合 ANSI 标准时,定义为 1。 |
示例
让我们尝试以下示例 -
#include <stdio.h> int main(){ printf("File: %s ", __FILE__ ); printf("Date: %s ", __DATE__ ); printf("Time: %s ", __TIME__ ); printf("Line: %d ", __LINE__ ); printf("ANSI: %d ", __STDC__ ); }
输出
运行此代码时,将产生以下输出 -
File: main.c Date: Mar 6 2024 Time: 13:32:30 Line: 8 ANSI: 1
预处理器运算符
C 预处理器提供以下运算符来帮助创建宏 -
示例:C 中的宏延续运算符 (\)
宏通常限制在一行中。宏延续运算符 (\) 用于延续过长而无法在一行中显示的宏。请看以下示例 -
#include <stdio.h> #define message_for(a, b) \ printf(#a " and " #b ": We love you! ") int main() { message_for(Ram, Raju); }
输出
运行此代码时,将产生以下输出 -
Ram and Raju: We love you!
示例:C 语言中的字符串化运算符 (#)
字符串化运算符 (#),也称为数字符号运算符,在宏定义中使用时,可将宏参数转换为字符串常量。
字符串化运算符只能在具有指定参数或参数列表的宏中使用。例如 -
#include <stdio.h> #define message_for(a, b) \ printf(#a " and " #b ": We love you! ") int main() { message_for(Carole, Debra); return 0; }
输出
运行代码并检查其输出 −
Carole and Debra: We love you!
示例:C 语言中的标记粘贴运算符 (##)
宏定义中的标记粘贴运算符 (##) 可合并两个参数。它允许将宏定义中的两个独立标记合并为一个标记。例如 -
#include <stdio.h> #define tokenpaster(n) printf ("token" #n " = %d", token##n) int main() { int token34 = 40; tokenpaster(34); return 0; }
输出
运行此代码时,将产生以下输出 -
token34 = 40
示例:C 语言中的 Defined() 运算符
预处理器 Defined 运算符用于常量表达式中,用于判断标识符是否已使用 #define 定义。如果指定的标识符已定义,则值为 True(非零)。如果符号未定义,则值为 false(零)。
以下示例展示了如何在 C 程序中使用 defined 运算符 -
#include <stdio.h> #if !defined (MESSAGE) #define MESSAGE "You wish!" #endif int main() { printf("Here is the message: %s ", MESSAGE); return 0; }
输出
运行代码并检查其输出 −
Here is the message: You wish!
C 语言中的参数化宏
CPP 的强大功能之一是能够使用参数化宏模拟函数。例如,我们可能有一些代码用于计算数字的平方,如下所示 -
int square(int x) { return x * x; }
我们可以使用宏重写上述代码,如下所示 -
#define square(x) ((x) * (x))
带有参数的宏必须先使用 #define 指令定义,然后才能使用。参数列表括在括号中,并且必须紧跟在宏名称之后。宏名和左括号之间不允许有空格。
示例
以下示例演示了如何在 C 中使用参数化宏 -
#include <stdio.h> #define MAX(x,y) ((x) > (y) ? (x) : (y)) int main() { printf("Max between 20 and 10 is %d ", MAX(10, 20)); return 0; }
输出
运行此代码时,将产生以下输出 -
Max between 20 and 10 is 20