必须用sizeof(void*)做预处理器分支,因其是编译期常量表达式,值为4或8可明确区分32/64位目标ABI,不依赖运行时API、头文件或平台宏,所有主流编译器均保证该行为,且C++98起可用。

直接看 sizeof(void*),别信 sizeof(int) 或运行时 if 判断——它不是“检测系统”,而是决定你代码在哪个 ABI 下编译生效。
为什么必须用 sizeof(void*) 做预处理器分支
因为它是编译期常量表达式,所有主流编译器(MSVC/GCC/Clang)都保证:sizeof(void*) == 8 表示目标为 64 位 ABI,sizeof(void*) == 4 表示 32 位。它不依赖头文件、不触发宏污染、C++98 起就可用。
常见错误包括:
- 写
if (sizeof(void*) == 8) { ... }—— 这是运行时语句,两边代码仍会被编译,可能链接失败(比如引用只在 64 位声明的符号) - 用
INTPTR_MAX或SIZE_MAX做#if条件 —— 它们不是预处理器字面量,MSVC 和 GCC 对十六进制宏展开行为不一致,极易出错 - 依赖
_WIN64或__x86_64__单独判断位数 —— 这些是平台+架构耦合宏,跨平台不通用(比如 macOS ARM64 不定义__x86_64__)
#if sizeof(void*) == 8 的实际写法与典型场景
必须配合 #if/#else 做编译期裁剪,常见于指针转整数、内存对齐、结构体打包等场景。
立即学习“C++免费学习笔记(深入)”;
例如处理地址映射:
#if sizeof(void*) == 8
uint64_t addr = reinterpret_cast<uint64_t>(ptr);
static_assert(alignof(std::max_align_t) >= 8, "need 8-byte alignment");
#else
uint32_t addr = reinterpret_cast<uint32_t>(ptr);
static_assert(alignof(std::max_align_t) >= 4, "need 4-byte alignment");
#endif
注意点:
- 不能把
static_assert写在#if外面,否则未满足条件的分支仍会触发断言失败 - 若需同时区分 Windows/Linux,应先用
#ifdef _WIN32分平台,再在各自分支内用sizeof(void*)分位数 - 交叉编译时,该值反映的是
-m32/-m64目标,不是宿主机系统位数
哪些宏看似能用,但实际要绕开
这些宏容易让人误以为能直接判断位数,但都有硬伤:
-
_WIN64:仅 Windows MSVC/Clang 支持,GCC on Windows(MinGW)不定义;且 Linux/macOS 完全不适用 -
__x86_64__和__aarch64__:只说明 CPU 架构,不等于 ABI 位数(理论上存在 32 位模式下的 x86_64 CPU,虽极少见) -
SIZE_MAX:值依赖size_t定义,而某些嵌入式平台size_t可能被 typedef 为unsigned long long即使是 32 位环境 -
__POINTER_WIDTH__:Clang 有,GCC 没有,MSVC 更没有,不可移植
真正需要“运行时检测本机系统位数”的情况极少——绝大多数需求其实是要适配当前编译目标的 ABI。一旦混淆了“编译目标”和“运行环境”,后续在容器、WSL、交叉构建中就会出现静默错误。最稳妥的做法,就是把 sizeof(void*) 当作唯一可信源,并始终用预处理器做分支。


















