C语言预处理完全指南
预处理概述
预处理是 C 编译流程的第一步,负责处理以 # 开头的预处理指令。预处理在编译前完成文本替换、条件选择等工作,是 C 语言强大而独特的特性。
宏定义
宏定义使用 #define 指令,可以定义常量、函数宏、类型别名等:
// 常量宏 #define PI 3.1415926 #define MAX_SIZE 1024 // 函数宏(带参数) #define MAX(a, b) ((a) > (b) ? (a) : (b)) #define SWAP(a, b) do { typeof(a) _t = a; a = b; b = _t; } while(0) // 避免重复计算,使用 typeof 获取类型 #define MIN(a, b) ({ \ typeof(a) _a = (a); \ typeof(b) _b = (b); \ _a < _b ? _a : _b; \ }) // 字符串化:# 将参数转为字符串 #define PRINT_INT(x) printf(#x " = %d\n", x) // PRINT_INT(val) 展开为 printf("val = %d\n", val) // 连接符:## 拼接标识符 #define DECLARE_GETTER(type, name) \ type get_##name(void) { return name; } // DECLARE_GETTER(int, count) 展开为 get_count 函数
条件编译
条件编译根据条件选择性地编译代码,常用于跨平台兼容、调试开关等:
// 根据平台选择代码 #ifdef __linux__ // Linux 平台代码 #define PATH_SEP '/' #elif _WIN32 // Windows 平台代码 #define PATH_SEP '\\' #endif // 调试开关 #ifdef DEBUG printf("Debug: x = %d\n", x); #endif // 防止头文件重复包含 #ifndef MY_HEADER_H #define MY_HEADER_H // 头文件内容 #endif // 编译时断言(C11 后也可用 _Static_assert) #define COMPILE_ASSERT(expr) \ typedef int static_assert_[(expr) ? 1 : -1]
预定义宏
C 标准定义了一些有用的预定义宏:
__FILE__ // 当前源文件名(字符串) __LINE__ // 当前行号(整数) __DATE__ // 编译日期(字符串) __TIME__ // 编译时间(字符串) __STDC__ // 是否符合 ANSI C 标准 __STDC_VERSION__ // C 标准版本(如 201112L 表示 C11)
文件包含
#include 指令用于包含其他文件内容:
// 尖括号:系统头文件,编译器从系统路径搜索 #include#include // 双引号:用户头文件,先从当前目录搜索 #include "myheader.h" #include "subdir/config.h" // 防止重复包含的完整写法 #ifndef MY_HEADER_H #define MY_HEADER_H #endif
宏与函数的区别
宏是文本替换,没有函数调用开销,但可能产生副作用:
// 风险:参数被求值多次 #define SQUARE(x) ((x) * (x)) SQUARE(++i); // 展开为 ((++i) * (++i)),i 被递增两次! // 安全写法:使用 GNU C 扩展 typeof #define SQUARE(x) ({ typeof(x) _x = (x); _x * _x; }) // 或者直接使用内联函数(C99+) static inline int square(int x) { return x * x; }
常见应用场景
1. 跨平台代码:根据操作系统、编译器选择不同实现
2. 性能关键代码:用宏替代函数调用减少开销
3. 代码生成:减少重复代码,如创建 getter/setter
4. 调试与日志:根据调试开关选择是否输出调试信息
5. 版本控制:根据功能开关选择编译不同模块