C语言中的#define是预处理指令的一种,在程序编译之前由预处理器处理,主要作用是进行文本替换,可以用来定义常量、宏函数、条件编译标识等,是C语言开发中非常常用的语法元素。

#define的基本语法
#define的基本语法格式如下,其中#define和宏名之间、宏名和替换文本之间都需要用空格分隔,替换文本不需要以分号结尾,预处理器会直接将代码中出现的宏名替换为对应的文本。
#define 宏名 替换文本
下面是一个最基础的常量定义示例,定义了圆周率PI的值为3.1415926,后续代码中所有出现PI的地方都会被替换为3.1415926。
#include <stdio.h>
#define PI 3.1415926
int main() {
double radius = 5.0;
double area = PI * radius * radius;
printf("圆的面积是: %fn", area);
return 0;
}
无参宏的使用
无参宏就是不带参数的宏定义,除了定义常量之外,还可以用来定义一些简短的代码段或者标识,常用于条件编译的场景。
定义常量
使用无参宏定义常量可以避免魔法数字,提升代码的可读性和可维护性,当需要修改常量值时只需要修改#define处的替换文本即可,不需要逐一修改代码中的对应值。
#include <stdio.h>
// 定义数组最大长度
#define MAX_ARRAY_LEN 100
// 定义错误码
#define ERROR_NULL_PTR -1
int main() {
int arr[MAX_ARRAY_LEN];
int ret = ERROR_NULL_PTR;
if (ret == ERROR_NULL_PTR) {
printf("指针为空错误n");
}
return 0;
}
条件编译标识
无参宏经常和#ifdef、#ifndef、#endif等预处理指令配合使用,实现条件编译,比如区分调试模式和发布模式的代码。
#include <stdio.h>
#define DEBUG
int main() {
#ifdef DEBUG
printf("当前是调试模式n");
#else
printf("当前是发布模式n");
#endif
return 0;
}
带参宏的使用
带参宏类似函数,宏名后面跟着用括号包裹的参数列表,替换文本中可以引用这些参数,预处理器会按照文本替换的规则处理参数,不会进行类型检查。
带参宏的基本语法如下:
#define 宏名(参数1, 参数2, ...) 替换文本
下面是一个计算两个数最大值的带参宏示例:
#include <stdio.h>
#define MAX(a, b) ((a) > (b) ? (a) : (b))
int main() {
int x = 10, y = 20;
printf("最大值是: %dn", MAX(x, y));
// 预处理器替换后相当于 printf("最大值是: %dn", ((x) > (y) ? (x) : (y)));
return 0;
}
注意带参宏的参数和整个表达式都需要用括号包裹,避免出现运算符优先级导致的错误,比如如果没有给参数加括号,当传入的表达式带有运算符时就会出现问题。
#include <stdio.h>
// 错误的定义方式,没有给参数加括号
#define MUL_WRONG(a, b) a * b
// 正确的定义方式
#define MUL_CORRECT(a, b) ((a) * (b))
int main() {
int x = 2, y = 3;
// 错误调用,替换后为 2 + 3 * 4 + 5,结果是19,不符合预期
printf("错误结果: %dn", MUL_WRONG(x + 3, y + 5));
// 正确调用,替换后为 ((2 + 3) * (4 + 5)),结果是45,符合预期
printf("正确结果: %dn", MUL_CORRECT(x + 3, y + 5));
return 0;
}
#define的注意事项
- 宏定义不需要分号结尾,如果加了分号,分号也会被当作替换文本的一部分,可能导致语法错误。
- 带参宏不会进行类型检查,参数可以是任何类型,只要替换后的文本符合语法即可,但是也容易因为类型不匹配导致问题。
- 宏定义的作用域是从定义处开始到文件结束,如果需要提前结束宏的作用域,可以使用
#undef指令取消宏定义。 - 宏展开是简单的文本替换,不会计算参数的值,也不会有函数调用的开销,但是可能会增加代码的体积。
- 避免定义过于复杂的带参宏,复杂的逻辑建议使用函数实现,否则会降低代码的可读性,也容易出现难以排查的错误。
常用预处理运算符
在使用#define时,还可以配合一些预处理运算符实现更灵活的功能:
| 运算符 | 作用 | 示例 |
|---|---|---|
| # | 将参数转换为字符串字面量 | #define STR(x) #x,STR(hello)会被替换为"hello" |
| ## | 连接两个 token | #define CONCAT(a, b) a##b,CONCAT(num, 1)会被替换为num1 |
| defined | 判断某个宏是否已经被定义,配合条件编译使用 | #if defined(DEBUG)等价于#ifdef DEBUG |
下面是#运算符的使用示例:
#include <stdio.h>
#define PRINT_VAR(x) printf(#x "的值是: %dn", x)
int main() {
int num = 100;
PRINT_VAR(num); // 替换后为 printf("num" "的值是: %dn", num); 输出 num的值是: 100
return 0;
}