1.预处理的概念
预处理的编译就是对C语言的源程序进行编译前,有“编译预处理程序”对预处理命令进行处理的过程。
C语言提供了多种预处理功能,如宏定义、文件包含、条件编译等。
2. 宏定义的基本概念
宏定义是C语言预处理指令之一,使用#define
指令实现,用于在编译前进行文本替换。
基本语法
#define 宏名 替换文本
示例
#define PI 3.1415926
#define MAX_SIZE 100
3. 宏定义的分类
3.1 无参宏(对象式宏)
#define BUFFER_SIZE 1024
#define VERSION "1.0.0"
3.2 带参宏(函数式宏)
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define SQUARE(x) ((x) * (x))
4. 宏定义的特点
-
预处理阶段替换:在编译前进行文本替换
-
无类型检查:宏只是简单的文本替换
-
不分配内存:宏定义不占用内存空间
-
作用域从定义处到文件结束(可用
#undef
取消定义)
5. 带参宏的注意事项
5.1 参数括号
为避免运算符优先级问题,参数和整个表达式都应加括号:
// 错误的示例
#define SQUARE(x) x * x
// 正确的示例
#define SQUARE(x) ((x) * (x))
5.2 避免副作用
宏参数可能被多次求值,导致副作用:
#define MAX(a, b) ((a) > (b) ? (a) : (b))
int x = 1, y = 2;
int z = MAX(x++, y++); // 可能导致x或y被多次递增
5.3 多行宏定义
使用反斜杠\
实现多行宏:
#define SWAP(a, b) do { \
typeof(a) temp = a; \
a = b; \
b = temp; \
} while(0)
6. 特殊宏运算符
6.1 字符串化运算符#
将宏参数转换为字符串:
#define STR(x) #x
printf("%s", STR(hello)); // 输出 "hello"
6.2 连接运算符##
连接两个标记:
#define CONCAT(a, b) a##b
int xy = 10;
printf("%d", CONCAT(x, y)); // 输出 xy的值 10
7. 预定义宏
C语言提供了一些预定义宏:
__LINE__ // 当前行号
__FILE__ // 当前文件名
__DATE__ // 编译日期
__TIME__ // 编译时间
__STDC__ // 是否遵循ANSI C标准
__func__ // 当前函数名(C99)
8. 宏与函数的比较
特性 | 宏 | 函数 |
---|---|---|
执行时机 | 预处理阶段 | 运行时 |
类型检查 | 无 | 有 |
开销 | 代码膨胀 | 调用开销 |
调试 | 难以调试 | 易于调试 |
参数求值 | 可能多次求值 | 只求值一次 |
适用场景 | 简单操作、常量定义等 | 复杂逻辑、重用代码等 |
9. 宏的常见用途
-
定义常量
-
条件编译
-
简化复杂表达式
-
泛型编程(通过宏模拟)
-
调试信息输出
-
平台特定代码处理
10. 最佳实践
-
宏名全部大写以区分变量和函数
-
带参宏的每个参数和整体表达式都要加括号
-
避免使用会产生副作用的参数
-
复杂逻辑优先考虑使用函数而非宏
-
适当使用
do {...} while(0)
包裹多语句宏 -
及时使用
#undef
取消不再需要的宏定义