C 语言中的预处理器宏作为语言的一部分出现,旨在提高源代码的可移植性、可读性和配置便利性。通过 #define、#ifdef、#ifndef 指令,可以创建条件代码段、声明常量和包含外部文件 (#include)。
问题的历史:
引入预处理器指令是为了简化源代码在不同系统和编译器之间的适配,同时自动化重复的操作。
问题:
没有预处理器,无法抽象平台差异,重复代码片段,防止头文件的重复包含。此外,必须小心,因为宏没有类型和作用域。
解决方案:
使用预处理器宏来声明常量、内联函数、条件编译并防止头文件的多次包含。
代码示例:
#ifndef MY_HEADER_H #define MY_HEADER_H #define MAX_SIZE 100 #ifdef DEBUG #define LOG(x) printf("%s\n", x) #else #define LOG(x) #endif #endif /* MY_HEADER_H */
主要特点:
如果在头文件中忘记了包含保护,会发生什么?
头文件可能会在一个 .c 文件中被多次包含(通过其他 .h 隐式包含),这将导致重新定义错误。
#define 宏和内联函数的声明有什么区别?
#define 仅仅是文本替换,不检查类型和语法的正确性,而内联函数则是经过编译器在类型分析阶段检查的普通函数。
宏可以包含副作用吗?
可以,如果宏定义不当,则传递的表达式可能会被计算多次。例如:
#define SQUARE(x) (x) * (x) int a = SQUARE(++i); // ++i 将被执行两次!
使用未加括号的宏,并传递产生副作用的表达式:
#define DOUBLE(x) x + x int result = DOUBLE(1+2); // 结果不是 6,而是 1+2+1+2=6?不,是 1+2+1+2=6 — 是吗?不,结果是 1+2+1+2。 // 但实际上会变成 1 + 2 + 1 + 2 = 6,但如果是 DOUBLE(++i),那么 ++i 会被执行两次。
优点:
缺点:
定义带括号的宏,并用于简单常量:
#define DOUBLE(x) ((x) + (x)) #define BUFFER_SIZE 1024
优点:
缺点: