亿迅智能制造网
工业4.0先进制造技术信息网站!
首页 | 制造技术 | 制造设备 | 工业物联网 | 工业材料 | 设备保养维修 | 工业编程 |
home  MfgRobots >> 亿迅智能制造网 >  >> Industrial programming >> C语言

深入理解 C 预处理器 (CPP):它如何简化编译过程

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

预处理器指令

所有预处理器命令都以井号(#)开头。它必须是第一个非空白字符,为了可读性,预处理指令应从第一列开始。下表列出了所有重要的预处理指令:

序号指令 & 说明
1

#define

定义预处理宏(文本替换)。

2

#include

插入另一个文件的头文件内容。

3

#undef

取消已定义的预处理宏。

4

#ifdef

若宏已定义则为真。

5

#ifndef

若宏未定义则为真。

6

#if

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

7

#else

#if 的替代分支。

8

#elif

将 #else 和 #if 合并为一条语句。

9

#endif

结束预处理条件编译。

10

#error

向 stderr 输出错误信息。

11

#pragma

以标准化方式向编译器发布特殊命令。

预处理器示例

通过以下示例理解各指令的用法。

#define MAX_ARRAY_LENGTH 20

该指示 CPP 将所有 MAX_ARRAY_LENGTH 替换为 20。使用 #define 定义常量可提高代码可读性。

#include <stdio.h>
#include "myheader.h"

第一行指示 CPP 从系统库获取 stdio.h 并将其文本插入当前源文件。第二行指示 CPP 从本地目录获取 myheader.h 并插入内容。

#undef FILE_SIZE
#define FILE_SIZE 42

指示 CPP 取消现有的 FILE_SIZE 定义,并将其重新定义为 42。

#ifndef MESSAGE
#define MESSAGE "You wish!"
#endif

仅当 MESSAGE 尚未定义时,才将其定义为 "You wish!"。

#ifdef DEBUG
/* 你的调试语句 */
#endif

若定义了 DEBUG,则处理其中的语句。这在编译时向 gcc 传递 -DDEBUG 标志时很有用,可随时开启或关闭调试功能。

预定义宏

ANSI C 定义了一些宏。虽然它们可在编程中使用,但预定义宏不应被直接修改。

序号宏 & 说明
1

__DATE__

当前日期,格式为 "MMM DD YYYY" 的字符字面量。

2

__TIME__

当前时间,格式为 "HH:MM:SS" 的字符字面量。

3

__FILE__

当前文件名的字符串字面量。

4

__LINE__

当前行号的十进制常量。

5

__STDC__

编译器符合 ANSI 标准时定义为 1。

尝试以下示例:

Live Demo
#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")

字符串化 (#) 运算符

字符串化运算符('#')在宏定义中使用时,可将宏参数转换为字符串常量。该运算符仅用于具有指定参数列表的宏。例如:

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

标记粘贴 (##) 运算符

标记粘贴运算符(##)可在宏定义中将两个参数合并。它允许宏定义中的两个独立标记连接成单个标记。例如:

Live Demo
#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);

此例展示了 token##n 连接成 token34,并同时使用了字符串化和标记粘贴。

Defined() 运算符

预处理器 defined 运算符用于常量表达式,判断标识符是否已通过 #define 定义。若已定义则值为真(非零),否则为假(零)。用法如下:

Live Demo
#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 定义。参数列表用括号括起,必须紧跟宏名,中间不能有空格。例如:

Live Demo
#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

C语言

  1. C# Hello World — 构建你的第一个C#应用程序
  2. C#关键字与标识符:规则、完整列表与最佳实践
  3. 精通C#变量与基础数据类型:完整指南
  4. C#运算符——C#运算符全面指南
  5. 掌握 C# 位运算与移位运算符:全面指南
  6. C# 基础:输入输出核心技巧完全指南
  7. C# 表达式、语句与代码块:全面指南与实战示例
  8. 掌握 C# 注释:类型、最佳实践与 XML 文档注释
  9. 掌握 C# switch 语句:语法、示例与最佳实践
  10. C# 三元运算符:条件表达式简明指南
  11. C# break 语句:用法、时机与实战示例
  12. 掌握 C# continue 语句:高效跳过循环迭代的实战指南