当前位置: 首页 > 文档资料 > 学习 C 语言 >

预处理器(Preprocessors)

优质
小牛编辑
137浏览
2023-12-01

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

所有预处理器命令都以井号(#)开头。 它必须是第一个非空白字符,并且为了便于阅读,预处理程序指令应该从第一列开始。 以下部分列出了所有重要的预处理程序指令 -

Sr.No.指令和说明
1

#define

替换预处理器宏。

2

#include

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

3

#undef

取消定义预处理器宏。

4

#ifdef

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

5

#ifndef

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

6

#if

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

7

#else

#if的替代方案。

8

#elif

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

9

#endif

结束预处理器条件。

10

#error

在stderr上打印错误消息。

11

#pragma

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

预处理器示例

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

#define MAX_ARRAY_LENGTH 20

该指令告诉CPP用20替换MAX_ARRAY_LENGTH的实例。使用#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")

The Stringize (#) Operator

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);

此示例显示令牌## n到token34的串联,这里我们使用了stringizetoken-pasting pasting。

The Defined() Operator

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

#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