C 语言中的预处理器


2021年8月23日, Learn eTutorial
2625

在本教程中,您将通过简单的示例学习 C 语言中的预处理器指令,如 #include、#define、#if 等。

C语言中的预处理器指令是什么?

C 语言中的预处理器指令,通常用术语“CPP”表示,以三种不同的方式工作。它用作

  • 文本替换工具,指示编译器将特定术语替换为一组已定义的指令。由于它允许用户定义宏,因此也称为宏处理器。
  • 插入工具,将其他文件的内容插入到源文件中。
  • 条件编译工具,通过从文件中删除代码段来实现。

在 C 语言中,我们必须先编译程序才能运行。预处理器是一种特殊类型的程序,它在程序被编译器处理之前处理程序的某些部分。这可以形象地表示如下

Preprocessors

井号(#)表示预处理器包含。虽然我们可以在程序的任何部分使用它,但通常的做法是在程序开头就定义它。

预处理器指令分类

C 语言的主要预处理器指令根据功能分为四组。

Preprocessors

1.包含指令

显然,您已经注意到,几乎没有哪个程序不使用 #include 预处理器。几乎每个程序都以这样的方式开始


#include <stdio.h>
main()
{
}
 

这意味着写在头文件 stdio.h 中的所有内置函数都可以在程序内部使用。同样,根据程序的性质,我们可以在程序中包含标准头文件和用户自定义的头文件。

 

2.宏指令

2.1 #define 指令

如本教程前面所讨论的,宏是可以被一个值替换的代码段。我们可以使用 #define 指令创建宏。语法是


#define token value 
 

在这里,预处理器的工作是在程序中的每个场合用指定的字符替换标识符。宏分为两种类型

  1. 类对象宏
  2. 类函数宏

类对象宏

通常用一个值替换标识符的宏被归类为类对象宏。最常用于表示数字常量。这里有一个例子


#define LIMIT 10 
 

这里,LIMIT 是宏名称,它替换了值 10。请观察下面的例子,它说明了类对象宏的工作原理。


#include<stdio.h>
#define LIMIT 10

int main()
{
    for(int i=1;i<=LIMIT;i++)
    {
        printf("%d\t",i);
    }
    return 0;
}
 

在这里,编译器会将 'LIMIT' 读作 10 并相应地处理程序,从而得到以下输出


1       2       3       4       5       6       7       8       9       10

类函数宏或参数化宏

参数化形式极大地增强了宏的能力,尤其是在执行数学计算方面。它使宏能够像基于变量的函数一样工作。假设我们想确定一个数的立方。为此,我们通常需要创建一个像这样的函数


int cube(int a)
{
   return a*a*a;
}
 

使用参数化宏,我们可以在一行中完成相同的操作。


#define cube(a) ((a) * (a) *(a)) 
 

这是示例


#include<stdio.h>
#define CUBE(a) ((a)*(a)*(a))

int main()
{
  printf("%d",CUBE(10));
  return 0;
}

 

输出


1000

我们必须记住,在程序中使用宏之前,我们必须使用 #define 指令来定义它们。

2.2 #undef 指令

C 中的 undef 指令用于取消定义一个宏,明确地说就是取消已经定义的宏。语法是


#undef token 
 

以下程序演示了 #undef 指令的工作原理。


#include<stdio.h>
#define CUBE(a) ((a)*(a)*(a))
#define LIMIT 20
#undef LIMIT
int main()
{
    printf("%d",CUBE(LIMIT));

    return 0;
}

 

在上面的代码片段中,我们取消了宏 LIMIT 的定义,因此当我们调用该函数时,输出显然会是一个错误,如下所示


error: 'LIMIT' undeclared (first use in this function)|

3.条件指令

C 编程中的条件指令用于检查程序中是否已经定义了某个宏,并指示预处理器是包含还是丢弃这组代码。我们可以借助指令 #ifdef、#ifndef、#if、#else、#elif、#endif 来实现条件编译。现在让我们逐一了解它们。

3.1 #ifdef 指令

#ifdef 预处理器指令检查是否存在由 #define 定义的宏,如果条件满足,则允许编译一段代码。否则,这部分代码将不会被编译。#ifdef 的原型如下


#ifdef MACRO
    //Set of codes
#endif
 

3.2 #ifndef 指令

该指令检查宏是否不存在,并执行放置在 ifndef 和 endif 之间的代码集。语法如下


#ifndef MACRO
    //Set of codes
#endif
 

现在让我们观察一下如何在程序中实现 #ifdef 和 ifndef。


#include<stdio.h>
#define CUBE(a) ((a)*(a)*(a))
#define LIMIT 20


int main()
{
    #ifdef LIMIT
    printf("Given limit is %d",LIMIT);
    #endif // LIMIT

    #ifndef CUBE
    printf("Cube %d :",CUBE(3));
    #endif // CUBE

    return 0;
}

 

输出


Given limit is 20

3.3 #if, #else 和 #elif

所有这些指令仅在满足指定条件或表达式时才执行代码。

#if 的语法如下


#if macro_expression
    //Set of codes
#endif
 

#else 的语法


#if macro_expression
    //Set of codes for if part
#else
 // set of codes for else part
#endif
 

#elif 的语法


#if macro_expression
    //Set of codes for if part
#elif
 // set of codes for elif part
#else
 //set of codes for else part
#endif
 

#include<stdio.h>
#include<conio.h>

#define AGE 

int main()
{

    #if AGE >=18
    printf("YOU ARE ELIGIBLE FOR VOTING");
    #else
    printf("YOU ARE NOT ELIGIBLE FOR VOTING");
    #endif
}

预处理器运算符

有不同类型的预处理器运算符。现在,我们将讨论最常用的几种。它们是

i. 宏续行 (\) 运算符

通常,像 #define 这样的宏会一直工作到遇到新行为止。有时我们需要宏足够大以容纳所有字符,而将所有这些写在单行中会影响程序的可读性。续行运算符('\')告诉编译器将下一行视为前一行的部分。这里有一个例子:


//Continuation Operator in C

#include<stdio.h>
# define CUBE(a) \
        printf("Cube of %d is %d\n",a,((a)*(a)*(a)));
int main()
{
    CUBE(6);
    return 0;
}
 

输出


Cube of 6 is 216

ii. 字符串化 (#) 运算符

字符串化运算符是预处理器运算符之一,用于操作字符串。顾名思义,宏的字符串化运算符能够将参数转换为字符串常量。此运算符仅适用于带参数的宏。


#define Macro_name(arg) #arg
 

考虑以下示例


#include <stdio.h>
#define display(text)  #text

int main(void)
 {
    printf(display(WELCOME TO LEARN E TUTORIALS));
    return 0;
}
 

输出


WELCOME TO LEARN E TUTORIALS

解释:在这段代码中,我们向 printf() 函数传递了一个宏。由于宏在程序编译前被处理,预处理器会将 display(WELCOME TO LEARN E TUTORIALS) 展开为 "WELCOME TO LEARN E TUTORIALS"。然后我们的 printf() 函数就变成了 printf("WELCOME TO LEARN E TUTORIALS"),因此得到了这个输出。

iii. 标记粘贴 (##) 运算符

标记粘贴运算符是一种预处理器运算符,用于将宏内部作为参数给出的两个独立的标记粘合在一起。符号 '##' 充当连接器,通过组合其两侧的标记。## 运算符可用于以下标记

1. 标识符,如变量名、函数名等。 2. 关键字和变量名,如 int、while、volatile 等。 3. 数据类型,如字符串、数字、字符、true 或 false。 4. 数学运算符和标点符号,如 (,=,); 等。

 

语法


#define Macro_name(arg1,arg2,...,argN) arg1##arg2##...##argN
 

检查以下程序


#include <stdio.h>

#define join(x, y)  x##y

int main()
{
 printf("join(20, 30) = %d\n", join(20, 30));

 return 0;
}
 

输出


join(20, 30) = 2030

iv. defined() 运算符

该运算符的工作是检查标识符是否已由 #define 定义。它有两个二进制输出:true 和 false。如果标识符存在,它返回 1。否则,它将标记为 '0'。这里是一个描述 #defined 应用的例子。


// defined operator
#include <stdio.h>
#if !defined (FUNCTION)
      #define FUNCTION "Hello World!!"
       #endif

int main(void)
 {
          printf("\n\t %s ", FUNCTION);
          return 0;
}
 

输出


Hello World!!

在这里,#defined 会查找 'FUNCTION' 宏。如果未找到,则会创建一个新的。之后,在 main 函数下,编译器会将 "FUNCTION" 这个词读作 "Hello World!"。

探索更多
#line 指令
#pragma 指令
#error 指令