宏定义必须加括号防展开错误,换行用\且后无字符,避免变量声明和自增/自减;优先用constexpr替代常量宏,函数式宏改用constexpr函数;仅编译期条件、文件包含、字符串化时才需宏。

宏定义写错括号会直接编译失败
预处理器不检查语法,只做文本替换,#define 后面少个括号或漏了反斜杠,错误往往藏在展开后才暴露——比如宏里调用函数却没加括号,MAX(a, b) a > b ? a : b 这种写法在 2 * MAX(3, 4) 中会展开成 2 * 3 > 4 ? 3 : 4,结果完全不对。
- 所有带参数的宏必须给整个表达式加括号:
#define MAX(a, b) ((a) > (b) ? (a) : (b)) - 宏体换行必须用
\,且\后不能有任何字符(包括空格、注释) - 避免在宏里声明变量或使用自增/自减操作符,比如
INC(x)展开为x++,在INC(i) + INC(i)中行为未定义
用 constexpr 替代简单宏更安全
现代 C++ 里,#define PI 3.14159 这类常量宏早该淘汰了。它没有类型、不进调试符号、无法取地址,还可能被意外重定义。
- 用
constexpr double PI = 3.14159;,类型清晰,支持调试,还能参与模板推导 - 函数式宏如
#define SQUARE(x) ((x) * (x))改成constexpr auto square(auto x) { return x * x; }(C++20),编译期求值、类型推导、无副作用 - 只有涉及编译期条件(
#ifdef)、文件包含或字符串化(#操作符)时,才非用宏不可
#ifdef 和 #if defined() 行为不完全等价
看起来一样,但嵌套和逻辑组合时容易翻车。比如 #if defined(A) && !defined(B) 是合法的,而 #ifdef A && !defined(B) 直接报错。
-
#ifdef只能单测一个标识符,不支持运算符;#if defined()是完整表达式,可嵌套、可与1/0混用 - 跨平台判断常用
#if defined(__linux__) || defined(_WIN32),不能写成多个#ifdef堆叠 - 用
#pragma once替代#ifndef XXX_H头卫,更简洁,主流编译器都支持,但极端老旧环境仍需传统卫士
宏的字符串化和连接操作符很危险
# 把参数变字符串字面量,## 拼接 token,看着方便,实际极易出错:参数本身是宏时不会展开,拼接后可能产生非法标识符。
立即学习“C++免费学习笔记(深入)”;
-
#define STR(x) #x中,STR(__LINE__)展开为"__LINE__"而不是行号字符串,要两层宏才能触发展开 -
#define CONCAT(a, b) a##b中,CONCAT(int, _t)得到int_t没问题,但CONCAT(123, abc)会报错:数字开头不能当标识符 - 日志宏常用
__FILE__和__LINE__,务必用STR()包一层再套一层宏来确保展开,否则打出来的永远是字面量
宏的本质是文本替换,不是语言特性。越想让它“智能”,越容易掉进展开顺序、作用域、类型擦除的坑里。真需要灵活性,优先考虑 constexpr、模板别名或内联函数——宏只该留在编译开关、头文件卫士和极少数字符串反射场景里。








