C++ 中 #define 用法大全

在 C++ 中,#define 作为预处理指令,核心功能是实现文本替换,虽不如 constinline 等 C++ 特性安全(缺乏类型检查),但在代码简化、条件编译、代码片段封装等场景仍有不可替代的作用。以下是其在 C++ 中的完整用法分类及示例。

一、基础用法:定义常量/符号

#define 替换固定值或无意义符号,提升代码可读性和维护性(需注意:C++ 中优先推荐 const 定义常量,因 const 有类型检查,更安全)。

语法

#define 标识符 替换内容  // 末尾无分号!

示例

// 1. 数值常量
#define PI 3.141592653589793  // 圆周率
#define MAX_SIZE 1024         // 数组最大长度
#define MIN_AGE 18            // 最小年龄限制

// 2. 字符串常量
#define APP_NAME "MyCppApp"   // 应用名称
#define LOG_PATH "./log.txt"  // 日志文件路径

// 3. 字符常量
#define SEPARATOR ','         // 数据分隔符
#define ESC_KEY '\033'        // ESC 键的 ASCII 码

// 4. 无意义符号(仅作标记)
#define ENABLE_DEBUG 1        // 调试模式开关(配合条件编译)

注意

  • 若加末尾分号,会导致替换错误:例如 #define MAX 100;,使用时 int a = MAX; 会变成 int a = 100;;,多一个分号引发语法错误。
  • const 的区别:const int MAX = 100; 是有类型的常量,编译器会检查类型匹配;#define MAX 100 是纯文本替换,无类型检查(如 float f = MAX; 虽能运行,但逻辑上不严谨)。

二、进阶用法:带参数的宏(模拟函数)

定义带参数的宏,实现简单逻辑的代码片段替换,无函数调用开销(预处理时直接替换代码),适合高频、简单的操作(复杂逻辑建议用 inline 函数,有类型检查)。

语法

#define 宏名(参数1, 参数2, ...) (替换逻辑)

核心原则

所有参数和整体结果必须加括号,避免因运算符优先级导致逻辑错误!

示例

// 1. 简单运算:求两数最大值
#define MAX(a, b) ((a) > (b) ? (a) : (b))  // 括号避免优先级问题
// 错误写法:#define MAX(a,b) a>b?a:b(如 MAX(2+3, 4) 会变成 2+3>4?2+3:4,逻辑正确,但 MAX(2*3, 4+5) 若参数有运算仍需括号)

// 2. 求平方
#define SQUARE(x) ((x) * (x))
// 错误写法:#define SQUARE(x) x*x(如 SQUARE(2+3) 会变成 2+3*2+3=11,而非 25)

// 3. 交换两数(多行宏用 \ 连接,用 do-while(0) 包裹确保整体执行)
#define SWAP(a, b) do { \
    auto temp = (a);    \
    (a) = (b);          \
    (b) = temp;         \
} while(0)  // do-while(0) 确保宏在 if 后加 ; 仍正确(如 if(1) SWAP(x,y); 不会报错)

// 4. 打印日志(结合字符串化操作)
#define LOG(msg) cout << "[Log] " << __FILE__ << ":" << __LINE__ << " " << msg << endl;
// __FILE__ 和 __LINE__ 是 C++ 预定义宏,分别表示当前文件名和行号

注意

  • 避免参数副作用:若参数含自增/自减(如 MAX(i++, j++)),会导致参数被多次执行(替换后变成 ((i++) > (j++) ? (i++) : (j++))ij 被自增两次),逻辑错误。
  • 复杂逻辑优先用 inline:例如需要循环、分支的逻辑,inline 函数有类型检查,可读性和安全性更高。

三、高级用法:预处理运算符(# 和 ##)

C++ 预处理器支持两个特殊运算符,用于增强宏的灵活性:字符串化(#)标识符连接(##)

1. 字符串化运算符(#)

将宏的参数直接转换为字符串(双引号包裹)。

示例

#include <iostream>
using namespace std;

// 将参数转为字符串
#define TO_STR(x) #x

int main() {
    int num = 123;
    cout << TO_STR(num) << endl;  // 输出 "num"(参数名转字符串)
    cout << TO_STR(123 + 456) << endl;  // 输出 "123 + 456"(表达式转字符串)
    cout << TO_STR("hello") << endl;  // 输出 "\"hello\""(字符串会额外加一层引号)
    return 0;
}

2. 标识符连接运算符(##)

将宏的两个参数(或参数与固定字符)连接成一个新的标识符(变量名、函数名等)。

示例

#include <iostream>
using namespace std;

// 连接前缀和数字,生成变量名(如 var1、var2)
#define MAKE_VAR(prefix, num) prefix##num

// 连接函数名前缀和功能,生成函数名(如 print_int、print_float)
#define MAKE_FUNC(type) print_##type

void print_int(int x) { cout << "Int: " << x << endl; }
void print_float(float x) { cout << "Float: " << x << endl; }

int main() {
    // 生成变量 var1、var2
    int MAKE_VAR(var, 1) = 10;  // 等价于 int var1 = 10;
    int MAKE_VAR(var, 2) = 20;  // 等价于 int var2 = 20;
    cout << var1 << " " << var2 << endl;  // 输出 10 20

    // 调用生成的函数名
    MAKE_FUNC(int)(30);    // 等价于 print_int(30); 输出 "Int: 30"
    MAKE_FUNC(float)(3.14f);  // 等价于 print_float(3.14f); 输出 "Float: 3.14"
    return 0;
}

四、实用场景:条件编译控制

配合 #ifdef#ifndef#else#elif#endif 等预处理指令,实现代码的条件性包含/排除,常用于调试开关、跨平台适配、版本控制等场景。

常用组合

指令 功能
#ifdef X 若宏 X 已定义,则执行后续代码
#ifndef X 若宏 X 未定义,则执行后续代码
#else #ifdef/#ifndef 搭配,取反分支
#elif 多条件分支(类似 C++ 中的 else if
#endif 结束条件编译块

示例

#include <iostream>
using namespace std;

// 1. 调试开关:定义 DEBUG 则打印调试信息,否则不打印
#define DEBUG 1  // 注释此行则关闭调试模式

#ifdef DEBUG
    #define DEBUG_LOG(msg) cout << "[Debug] " << msg << endl;
#else
    #define DEBUG_LOG(msg)  // 空宏,替换后无任何代码
#endif

// 2. 跨平台适配:区分 Windows 和 Linux
#ifdef _WIN32  // Windows 系统预定义宏 _WIN32
    #define OS_NAME "Windows"
    #include <windows.h>
#elif __linux__  // Linux 系统预定义宏 __linux__
    #define OS_NAME "Linux"
    #include <unistd.h>
#else
    #define OS_NAME "Unknown"
#endif

int main() {
    DEBUG_LOG("Program start");  // DEBUG=1 时输出 "[Debug] Program start"
    cout << "OS: " << OS_NAME << endl;  // 输出当前系统名称
    return 0;
}

五、辅助用法:取消宏定义(#undef)

#undef 取消已定义的宏,之后该宏不再生效(可用于临时启用宏,后续关闭)。

示例

#include <iostream>
using namespace std;

#define MAX 100
cout << MAX << endl;  // 输出 100(宏生效)

#undef MAX  // 取消 MAX 宏定义
// cout << MAX << endl;  // 错误:MAX 未定义(宏已失效)

// 重新定义 MAX
#define MAX 200
cout << MAX << endl;  // 输出 200(新宏生效)

六、C++ 中使用 #define 的注意事项

  1. 优先用 C++ 特性替代
    • 常量定义:用 const(如 const double PI = 3.14;)或 constexpr(编译期常量,如 constexpr int MAX = 1024;),有类型检查。
    • 函数模拟:用 inline 函数(如 inline int MAX(int a, int b) { return a > b ? a : b; }),有类型检查和函数语义。
  2. 避免宏污染:宏是全局生效的,若宏名过于简单(如 MAXMIN),可能与其他库的宏冲突,建议加前缀(如 MY_APP_MAX)。
  3. 多行宏必须用 \ 连接:每行末尾加 \(注意 \ 后不能有空格),否则预处理会认为宏定义结束,导致语法错误。
  4. 警惕宏的副作用:参数含自增、自减、函数调用等有副作用的操作时,会导致逻辑错误(如 MAX(i++, j++))。

总结

#define 在 C++ 中虽不是“现代特性”,但在条件编译(如调试开关、跨平台)、简单代码片段封装(如日志打印)等场景仍非常实用。使用时需权衡其“灵活性”和“缺乏类型检查”的缺点,优先用 const/constexpr/inline 替代,仅在必要时使用 #define

0 条评论

目前还没有评论...