Cprogramming 简明教程

Preprocessors in C

C 预处理器不是编译器的一部分,而是编译过程中的一个单独步骤。简单来说,C 预处理器只是一个文本替换工具,它指示编译器在实际编译之前执行所需的预处理。我们称 C 预处理器为 CPP。

在 C 编程中, preprocessing 是 C 代码编译的第一步。它发生在 tokenization 步骤之前。预处理器的主要功能之一是包含 header files ,其中包含程序中使用的库函数。C 中的预处理器还定义常量和扩展宏。

C 中的预处理器语句称为 directives 。程序的预处理器部分始终出现在 C 代码的顶部。每个预处理器语句都以哈希 (#) 符号开头。

Preprocessor Directives in C

下表列出了所有重要的预处理程序指令:

Directive

Description

# define

Substitutes a preprocessor macro.

#include

从另一个文件中插入一个特定头。

#undef

Undefines a preprocessor macro.

#ifdef

如果定义了此宏,则返回 true。

#ifndef

如果未定义此宏,则返回 true。

#if

测试编译时条件是否为真。

#else

The alternative for #if.

#elif

在一个语句中使用 #else 和 #if。

#endif

Ends preprocessor conditional.

#error

在 stderr 上打印错误消息。

#pragma

使用标准化方法向编译器发出特殊命令。

Preprocessors Examples

分析以下示例以了解各种指令。

#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

以下指令告诉 CPP 处理在 DEBUG 定义时括起来的语句。

#ifdef DEBUG
   /* Your debugging statements here */
#endif

如果在编译时将 -DDEBUG 标志传递给 gcc 编译器,这很有用。这将定义 DEBUG,以便您可以在编译期间随时打开和关闭调试。

Predefined Macros in C

ANSI C 定义了许多宏。虽然每个宏都可用在编程中,但是预定义的宏不应该直接被修改。

Macro

Description

DATE

当前日期,字符文本格式为“MMM DD YYYY”。

TIME

当前时间,字符文本格式为“HH:MM:SS”。

FILE

此项包含当前文件名,字符串文本格式。

LINE

此项包含当前行号,十进制常量。

STDC

如果编译器符合 ANSI 标准,则定义为 1。

Example

让我们尝试以下示例:

#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__ );
}

当你运行这段代码时,它将产生以下输出:

File: main.c
Date: Mar 6 2024
Time: 13:32:30
Line: 8
ANSI: 1

Preprocessor Operators

C 预处理器提供以下运算符来帮助创建宏:

Example: The Macro Continuation (\) Operator in C

宏通常限制在一行中。宏延续运算符 (\) 用于继续一行中过长的宏。请看以下示例:

#include <stdio.h>

#define message_for(a, b)  \
   printf(#a " and " #b ": We love you!\n")

int main() {
   message_for(Ram, Raju);
}

当你运行这段代码时,它将产生以下输出:

Ram and Raju: We love you!

Example: The Stringize (

字符串化运算符(#),也称为井号运算符,在宏定义中使用时,会将宏参数转换为字符串常量。

字符串化运算符只能在具有指定参数或参数列表的宏中使用。例如:

#include <stdio.h>

#define  message_for(a, b)  \
   printf(#a " and " #b ": We love you!\n")

int main() {

   message_for(Carole, Debra);
   return 0;
}

运行代码并检查其输出:

Carole and Debra: We love you!

Example: The Token Pasting (

宏定义中的标记粘贴运算符 (##)将两个参数组合起来。它允许多个宏定义中的标记加入到单独的标记中。例如:

#include <stdio.h>

#define tokenpaster(n) printf ("token" #n " = %d", token##n)

int main() {

   int token34 = 40;
   tokenpaster(34);
   return 0;
}

当你运行这段代码时,它将产生以下输出:

token34 = 40

Example: The defined() Operator in C

预处理器定义的运算符用于常量表达式中,以确定标识符是否使用 #define 定义。如果指定了标识符,则值为 true(非零)。如果未定义符号,则值为 false(零)。

以下示例显示如何在 C 程序中使用 defined operator

#include <stdio.h>

#if !defined (MESSAGE)
   #define MESSAGE "You wish!"
#endif

int main() {

   printf("Here is the message: %s\n", MESSAGE);
   return 0;
}

运行代码并检查其输出:

Here is the message: You wish!

Parameterized Macros in C

CPP强大的功能之一是使用参数化宏模拟函数的能力。例如,我们可能有一些代码如下平方一个数字:

int square(int x) {
   return x * x;
}

我们可以使用以下宏重写上述代码:

#define square(x) ((x) * (x))

具有参数的宏必须在使用之前使用 #define 指令进行定义。参数列表用圆括号括起来,并且必须紧跟宏名称。宏名称和左括号之间不允许有空格。

Example

以下示例演示如何在 C 中使用参数化宏:

#include <stdio.h>

#define MAX(x,y) ((x) > (y) ? (x) : (y))

int main() {

   printf("Max between 20 and 10 is %d\n", MAX(10, 20));
   return 0;
}

当你运行这段代码时,它将产生以下输出:

Max between 20 and 10 is 20