楼层: 首页/ 软件技术/ C 语言/ 预处理与宏:编译前的文字替换
9

预处理与宏:编译前的文字替换

Preprocessor · #define · #include

所有以 # 开头的行,都是"预处理指令"——它们在真正编译之前跑,本质是文本替换。你写的 #define PI 3.14,预处理阶段会把代码里所有 PI 原样换成 3.14。理解这一点,宏的坑就都好懂了。

#define 宏:对象宏与函数宏

两种宏 + 经典陷阱

#define PI 3.14159 // 对象宏:纯替换 #define SQ(x) ((x) * (x)) // 函数宏:注意每个 x 都要加括号! // 危险示范:没加括号会怎样? #define BAD(x) x * x // BAD(1+2) 会被替换成 1+2*1+2 = 5,而不是 9!这就是为什么要套括号。 double r = 2; printf("%f\n", PI * SQ(r)); // 3.14159 * 4 = 12.56

谈宏 vs 函数

宏是纯文本替换,没有类型检查、不做计算,容易出括号和副作用的 bug(SQ(i++) 里 i 会自增两次)。
函数有类型检查、会真正算一遍、参数只求值一次。
现代 C 里,能用 static inline 函数或 constexpr(C23)代替的,就少用函数宏。

条件编译与头文件保护

头文件会被多个 .c 文件 #include,如果不保护,同一个声明会被复制好几份,链接时报"重复定义"。头文件保护宏就是干这个的。

头文件保护:每个 .h 文件都该长这样

// student.h #ifndef STUDENT_H // 如果没定义过 STUDENT_H #define STUDENT_H // 就定义它,并把下面内容包起来 struct Student { char name[20]; int age; }; #endif // 第二次再 include,STUDENT_H 已定义,整段被跳过

常用预定义宏(调试神器)

printf("文件:%s, 行号:%d, 函数:%s\n", __FILE__, __LINE__, __func__); // 自动告诉你这句代码在哪个文件、第几行、哪个函数里——打日志特别好用