应使用 #ifdef 检查 __linux__、_win32、__apple__ 等编译器预定义宏来判断平台,而非运行时 if;它们由编译器注入,与宿主系统无关,需注意宏名准确性、共存逻辑(如 __android__ 与 __linux__)、工具链配置及第三方头文件顺序。

怎么用 __linux__、_WIN32、__APPLE__ 判断平台
编译器在预处理阶段会自动定义特定宏,不是靠运行时检测,所以必须用 #ifdef 而不是 if。这些宏由编译器(如 GCC、Clang、MSVC)注入,和你装没装对应系统无关——比如在 Windows 上用 WSL 编译,__linux__ 依然有效,因为目标平台是 Linux。
-
_WIN32在 MSVC 和 MinGW 下都定义(包括 64 位),_WIN64仅 64 位 Windows;别用WIN32(旧版 MFC 宏,不可靠) -
__linux__是 GCC/Clang 的标准定义,linux(无双下划线)是错的,不会生效 -
__APPLE__在 macOS 和 iOS 编译时定义,但需配合__MACH__或TARGET_OS_MAC区分 Darwin 内核下的不同环境 - Android 需额外检查
__ANDROID__,它和__linux__共存,不能只靠后者判断是否为桌面 Linux
为什么 #ifdef __linux__ 有时不生效
常见原因是编译器没走预处理流程,或者用了错误的工具链。比如用 CMake 构建时,若 set(CMAKE_SYSTEM_NAME Android) 却没配 NDK 工具链,GCC 可能仍按主机系统(如 Linux)定义宏,导致 __ANDROID__ 没被定义。
- 用
g++ -dM -E /dev/null | grep -i linux直接看当前编译器实际定义了哪些宏 - CMake 中应使用
target_compile_definitions(mylib PRIVATE $:LINUX_BUILD>)而非硬写#ifdef,避免手误漏掉构建配置 - MinGW 环境下
_WIN32和__linux__不会同时出现,但某些交叉编译脚本会手动-D__linux__,这时要检查 Makefile/CMakeLists.txt 是否有冗余定义
文件路径分隔符和 API 选择:Windows vs Unix-like
跨平台最常翻车的是路径拼接和系统调用。C++ 标准库(C++17 std::filesystem)已解决一部分,但老项目或嵌入式环境仍得手动处理。
- 路径分隔符:Windows 用
'\',其他用'/';不要拼字符串,用std::filesystem::path构造,它会自动 normalize - 进程创建:
CreateProcess(Windows)和fork+exec(Unix)语义差异大,封装层必须抽象出统一接口,不能只靠宏切换函数名 - 线程本地存储:
__thread(GCC)、thread_local(C++11)在各平台支持度不一;MSVC 2015+ 支持thread_local,但 MinGW-w64 的旧版本可能 fallback 到__declspec(thread) - 大小端判断别依赖
__BYTE_ORDER__(Clang 特有),改用std::endian(C++20)或运行时uint32_t x = 1; bool is_little = *(char*)&x == 1;
第三方库头文件包含顺序引发的宏冲突
像 Qt、Boost 这类库会在头文件里定义自己的平台宏(如 Q_OS_WIN),如果它们出现在你自定义 #ifdef 之前,可能覆盖或干扰你的判断逻辑。
立即学习“C++免费学习笔记(深入)”;
- 永远把项目自己的平台头(如
platform.h)放在所有第三方头之前 - 避免在头文件里用
#undef清理系统宏,容易破坏下游依赖;应在.cpp文件里局部控制 - SQLite、zlib 等 C 库常通过
-DOS_UNIX之类传参控制行为,确保 CMake 的add_compile_definitions()和你代码里的宏逻辑一致 - macOS 上
statfs和 Linux 的statvfs参数类型不同,直接套用会编译失败,必须用#ifdef __APPLE__分支写两套字段访问逻辑
平台宏看着简单,但真正难的是边界情况:比如 WSL2 下该算 Windows 还是 Linux?iOS Simulator 是 __APPLE__ 但不是真设备,sysctlbyname("hw.ncpu") 会失败。这些没法靠一个宏兜住,得结合运行时探测和构建时约束一起用。











