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

以下指令告诉CPP如果定义了DEBUG,则处理包含的语句。

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

如果您在编译时向gcc编译器传递-DDEBUG标志,这将很有用。这将定义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\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

预处理器运算符

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

示例: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!

示例:C语言中的字符串化(#)运算符

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

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

#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!

示例: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\n", 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\n", MAX(10, 20));  
   return 0;
}

输出

运行此代码时,将产生以下输出:

Max between 20 and 10 is 20
广告