概念
macro(宏)是c/c++预处理器提供的一种纯文本替换机制, 通过 #define 定义
- 执行时机
在编译的预处理阶段展开, 不占用运行时内存, 无函数调用开销
- 核心缺陷
没有类型检查, 容易因运算符优先级和多次求值引发隐蔽bug
普通宏
#define 宏 实际内容
// 对象宏(常量)
#define PI 3.1415926
#define MAX_BUFFER_SIZE 1024
// 函数宏(带参数)
// 注意: 参数和整个表达式必须用括号包裹, 防止优先级错误
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
由于宏是纯文本替换, 若参数包含副作用表达式(如自增), 会导致多次求值
int a = 3, b = 4;
int c = MAX(a++, b);
// 展开为: (((a++) > (b)) ? (a++) : (b))
// 结果: a 会被递增两次, 导致严重 Bug!
宏操作符
#字符串化
将宏参数转换为字符串字面量
// STR(Hello) → "Hello"
#define STR(x) #x
##令牌粘贴
将两个宏参数拼接为一个标识符
// CONCAT(var, 1) → var1
#define CONCAT(a, b) a##b
预处理指令
预处理指令以 # 开头, 用于控制编译器的行为
条件编译
用于跨平台编译或控制调试代码的开关
| 指令 | 说明 |
|---|---|
# |
空指令, 无任何效果 |
#include |
包含一个源代码文件 |
#define 宏名 内容 |
定义宏 |
#undef 宏名 |
取消已定义宏 |
#if |
若给定条件为真,则编译下面代码 |
#ifdef 宏名#if defined (宏名) |
若宏已经定义,则编译下面代码 |
#ifndef 宏名 |
若宏没有定义,则编译下面代码 |
#elif |
若前面#if给定条件不为真, 且当前条件为真 |
#endif |
结束一个#if…#else条件编译块 |
- 示例, 头文件防重包含(include guards)
#ifndef __INCLUDE_HPP__
#define __INCLUDE_HPP__
// ...
#endif
- 示例, 取消宏定义
#define FLAG
// 等价于 #ifdef FLAG
#if defined(FLAG)
// ...
#endif
#undef FLAG
预定义宏 (内置宏)
编译器在编译时自动定义的宏, 常用于环境探测和调试
平台与操作系统探测
| 宏名 | 说明 |
|---|---|
_WIN32 |
在所有windows(32/64位)上都定义 |
_WIN64 |
仅在64位windows上定义 |
__linux__ |
在linux上定义 |
__unix__ |
在unix上定义 |
__APPLE__ |
在apple上定义 |
__ANDROID__ |
在 Android NDK 环境下定义 |
#include <iostream>
int main() {
#if defined(_WIN64)
std::cout << "windows 64" << std::endl;
#elif defined(__linux__)
std::cout << "linux" << std::endl;
#elif defined(__unix__)
std::cout << "unix" << std::endl;
#elif defined(__APPLE__)
std::cout << "apple" << std::endl;
#elif defined(__ANDROID__)
std::cout << "android" << std::endl;
#endif
return 0;
}
c++语言标准版本
__cplusplus 是c++编译器特有宏, 用于检测当前编译器支持的c++标准版本
| 值 | 说明 |
|---|---|
199711L |
c++98/03 |
201103L |
c++11 |
201402L |
c++14 |
201703L |
c++17 |
202002L |
c++20 |
#include <iostream>
int main() {
#if __cplusplus >= 202002L
std::cout << "c++20 or newer\n";
#elif __cplusplus >= 201703L
std::cout << "c++17\n";
#elif __cplusplus >= 201402L
std::cout << "c++14\n";
#elif __cplusplus >= 201103L
std::cout << "c++11\n";
#else
std::cout << "c++98/03 or older\n";
#endif
return 0;
}
源码定位与调试
| 宏 | 说明 | 标准支持 |
|---|---|---|
__LINE__ |
表示当前行号, 整型 | c/c++标准 |
__FILE__ |
表示当前文件名, 字符串类型 | c/c++标准 |
__DATE__ |
编译日期, 字符串类型 | c/c++标准 |
__TIME__ |
编译时间, 字符串类型 | c/c++标准 |
__FUNCTION__ |
当前函数名(编译器扩展, 等同于__func__) |
非标准(gcc/clang / MSVC 支持) |
__func__ |
当前函数名(仅函数名) | c99 / c++11 标准 |
__PRETTY_FUNCTION__ |
包含返回类型和参数的完整函数签名 | gcc/ clang 扩展 |
__FUNCSIG__ |
包含返回类型和参数的完整函数签名 | MSVC 扩展 |
#include <iostream>
void hello() {
std::cout << "file: " << __FILE__ << std::endl;
std::cout << "line: " << __LINE__ << std::endl;
std::cout << "date: " << __DATE__ << std::endl;
std::cout << "time: " << __TIME__ << std::endl;
std::cout << "func: " << __FUNCTION__ << std::endl;
}
int main() {
hello();
return 0;
}
现代 c++替代方案 (best practices)
在 c++ 中, 应尽量减少宏的使用, 利用现代语言特性替代宏, 以获得类型安全和更好的调试体验
| 宏的用途 | 传统 C/c++ 写法 | 现代 c++ 推荐替代方案 |
|---|---|---|
| 定义常量 | #define MAX_SIZE 100 |
constexpr int MAX_SIZE = 100; |
| 简单函数 | #define MAX(a,b) … |
template <typename T> inline T max(T a, T b) 或 std::max |
| 条件编译 | #ifdef DEBUG |
c++17 if constexpr 或 c++20 consteval (部分场景) |
| 获取代码位置 | __FILE__, __LINE__ |
c++20 std::source_location::current() |