C语言预处理详解:宏、条件编译与头文件保护

C语言预处理详解:宏、条件编译与头文件保护
LichermioneC语言预处理详解
1. 预定义宏
1 | printf("file=%s line=%d date=%s time=%s\n", |
常用预定义标识包括 __FILE__、__LINE__、__DATE__、__TIME__ 和标准版本相关宏。
2. 对象宏
1 |
宏是预处理阶段的文本替换,没有类型检查和独立作用域。对于有类型的常量,可根据场景考虑 const 或枚举。
3. 函数宏与括号
1 |
宏参数和整体都应加括号,否则:
1 |
|
4. 宏参数副作用
1 | int m = MAX(i++, j++); |
函数宏可能多次计算参数,使自增、函数调用等副作用重复发生。参数有副作用时不要传给会重复展开它的宏,优先使用普通函数或 inline 函数。
5. #与##
# 把宏参数转换为字符串:
1 |
|
## 连接两个记号:
1 |
|
6. 取消宏与命令行定义
1 |
编译时定义宏:
1 | gcc -DDEBUG=1 main.c -o app |
也可以用 -U 取消某个宏定义。
7. 条件编译
1 |
还可使用 #if、#elif、#else、#endif 和 defined,用于平台适配、功能开关和调试代码。
8. 头文件保护
1 |
|
它防止同一翻译单元重复包含头文件。#pragma once 更简洁且被广泛支持,但不属于传统C标准。
#include <file.h> 通常搜索系统路径;#include "file.h" 通常先搜索当前项目相关路径,再搜索系统路径。
9. 宏与函数的选择
宏可以接受不同类型并在预处理期展开,但没有类型检查,可能重复求值且难以调试。普通函数类型明确、只计算参数一次;现代编译器也能内联短函数。除条件编译、常量配置和必要的代码生成外,不应为了省一次调用盲目使用宏。
10. 总结
预处理发生在正式编译之前,本质是指令驱动的文本转换。写宏时要控制括号与副作用,组织头文件时要防止重复包含,并尽量把复杂逻辑留给具有类型检查的C函数。