C 预处理器
C - 预处理器
Section titled “C - 预处理器”C 预处理器(CPP)是一个程序,它在您的源代码传递给实际的编译器 之前 对其进行处理。它充当文本操作工具,解释称为 预处理指令 的特殊指令。
预处理指令以井号(#)开头,通常放在行首。它们不是 C 语句,并且不以分号结尾。
预处理器的常见用途包括:
- 文件包含: 包含头文件(
#include)。 - 宏定义: 定义符号常量和函数式宏(
#define)。 - 条件编译: 根据条件包含或排除部分代码(
#if、#ifdef、#ifndef、#else、#elif、#endif)。 - 其他指令: 移除宏定义(
#undef),发出错误(#error),提供编译器特定指令(#pragma)。
关键预处理指令
Section titled “关键预处理指令”| Directive | Description |
|---|---|
| 指令 | 描述 |
#include | 插入指定的头文件内容。<filename.h> 搜索标准系统目录;"myheader.h" 首先搜索当前目录,然后搜索标准目录。 |
#define | 定义一个预处理宏。可以定义符号常量(#define PI 3.14)或函数式宏(#define MAX(a,b) ((a) > (b) ? (a) : (b)))。在编译之前进行文本替换。 |
#undef | 移除先前定义的宏定义。 |
#ifdef MACRO_NAME | 条件编译:如果定义了 MACRO_NAME(使用 #define 定义),则包含后续代码。以 #endif 结束。 |
#ifndef MACRO_NAME | 条件编译:如果 未 定义 MACRO_NAME,则包含后续代码。常用于头文件中的包含守卫。以 #endif 结束。 |
#if constant_expression | 条件编译:如果 constant_expression 的计算结果非零(为真),则包含后续代码。以 #endif 结束。 |
#else | 在 #if、#ifdef、#ifndef 块中使用,为条件为假时提供替代代码。 |
#elif constant_expression | 否则如果。在 #if 块中使用,如果之前的 #if / #elif 为假,则测试另一个条件。 |
#endif | 标记条件编译块(#if、#ifdef、#ifndef)的结束。 |
#error message | 导致预处理器停止并发出包含指定文本的错误消息。在编译期间进行健全性检查时很有用。 |
#pragma directive | 向编译器提供实现定义的指令(例如,控制优化、警告、结构体对齐)。高度依赖于编译器。 |
文件包含(#include)
Section titled “文件包含(#include)”对于使用标准库函数和自定义头文件中的声明至关重要。
#include <stdio.h> // Include standard input/output library // 包含标准输入/输出库#include <stdlib.h> // Include standard library (malloc, exit, etc.) // 包含标准库 (malloc, exit 等)#include "my_utils.h" // Include a custom header file from the project // 从项目中包含自定义头文件宏定义(#define)
Section titled “宏定义(#define)”- 符号常量: 将宏名称的出现替换为定义的文本。
#define BUFFER_SIZE 1024#define COMPANY_NAME "My Tech Corp."
char buffer[BUFFER_SIZE];printf("Welcome to %s\n", COMPANY_NAME);- 函数式宏: 可以接受参数。警告: 请小心使用括号,以避免运算符优先级问题,并注意参数可能会被多次求值。
#define SQUARE(x) ((x) * (x)) // Parentheses are crucial! // 括号至关重要!#define IS_ODD(n) ((n) % 2 != 0)
int main(void) { int a = 5; printf("Square of %d is %d\n", a, SQUARE(a)); // Output: 25 // 输出: 25 printf("Square of %d is %d\n", a + 1, SQUARE(a + 1)); // Expands to ((a + 1) * (a + 1)) -> 36 // 展开为 ((a + 1) * (a + 1)) -> 36
if (IS_ODD(a)) { printf("%d is odd\n", a); // Output: 5 is odd // 输出: 5 is odd } return 0;}现代 C 语言通常更倾向于使用 const 变量表示常量和 inline 函数(或普通函数)来代替复杂的函数式宏,因为它们提供了更好的类型安全和调试性。
条件编译(#if、#ifdef 等)
Section titled “条件编译(#if、#ifdef 等)”允许根据编译时求值的条件来编译不同的代码段。这对于特定于平台的代码、调试版本或功能标志非常有用。
#include <stdio.h>
#define DEBUG_MODE 1 // Set to 1 for debug builds, 0 for release // 设置为 1 表示调试版本,0 表示发布版本#define TARGET_OS "Linux"
int main(void) {
#if DEBUG_MODE == 1 printf("Debug mode is ON\n"); // 调试模式开启#else printf("Release mode\n"); // 发布模式#endif
#ifdef TARGET_OS printf("Target OS defined as: %s\n", TARGET_OS); // 目标操作系统定义为:%s#else printf("Target OS not defined.\n"); // 目标操作系统未定义。#endif
#if defined(TARGET_OS) && (TARGET_OS[0] == 'L') // Check if defined and starts with 'L' // 检查是否已定义且以 'L' 开头 printf("Looks like a Linux target.\n"); // 看起来是 Linux 目标。#elif defined(TARGET_OS) && (TARGET_OS[0] == 'W') printf("Looks like a Windows target.\n"); // 看起来是 Windows 目标。#endif
#ifndef LICENSE_KEY // Check if LICENSE_KEY is NOT defined // 检查 LICENSE_KEY 是否未定义 #error "License key must be defined!" // Stop compilation // 许可证密钥必须定义!// 停止编译#endif
return 0;}您可以通过 #define 或使用编译器标志(例如,gcc -DDEBUG_MODE=1 mycode.c)从外部定义宏。
C 标准定义了一些有用的宏,它们提供有关编译环境的信息。这些宏不应被修改。
| Macro | Description |
|---|---|
| 宏 | 描述 |
__DATE__ | 编译日期,作为字符串字面量(例如,“Jan 01 2023”)。 |
__TIME__ | 编译时间,作为字符串字面量(例如,“10:30:00”)。 |
__FILE__ | 当前源文件名,作为字符串字面量。 |
__LINE__ | 源文件中的当前行号,作为整数常量。 |
__STDC__ | 如果编译器符合 C 标准,则定义为 1(具体版本可能取决于编译器标志)。 |
__STDC_VERSION__ | (C95+)定义为一个长整数,表示标准版本(例如,C11 为 201112L,C17 为 201710L)。 |
示例:
printf("Compiled on %s at %s\n", __DATE__, __TIME__);printf("Function '%s' is at %s:%d\n", __func__, __FILE__, __LINE__); // __func__ (C99) provides function name // __func__ (C99) 提供函数名称预处理器是 C 语言中用于代码组织和条件编译的强大工具。