最可靠的是用 _win32 和 __linux__ 判断操作系统:二者由编译器预处理阶段自动定义,纯编译期决策,不依赖运行环境;_win32 在所有 windows 工具链(msvc/mingw/clang)下均定义(含64位),__linux__ 是 gcc/clang 在 linux 目标下的事实标准宏,二者互斥且不可替代。

直接用 _WIN32 和 __linux__ 就够了,其他方式要么不可靠,要么是编译器或构建系统层面的干扰项。
为什么只认 _WIN32 和 __linux__
这两个宏由编译器在预处理阶段自动定义,不依赖运行环境、不查文件系统、不调用任何 API——纯编译期决策。MSVC、MinGW、Clang for Windows 全部定义 _WIN32(包括 x64);GCC/Clang 在 Linux 目标下必定义 __linux__(注意是双下划线,__linux__✅,__linux❌)。它们互斥:交叉编译时不会同时出现,也不受你当前在哪台机器上敲命令影响。
常见错误现象:
- 误用
WIN32:这是 CMake 的变量名,不是编译器宏;MSVC 不定义它,MinGW 通常也不定义 - 漏掉双下划线写成
linux或_linux:预处理器根本识别不了 - 把
__APPLE__放在__linux__后面判断:macOS 不定义__linux__,但某些旧版 Clang 可能误设__unix__,导致 macOS 被塞进#else分支
#ifdef _WIN32 后该接 #else 还是 #elif defined(__linux__)
取决于你是否需要为非 Windows 系统做差异化处理:
- 如果只分“Windows vs 其他”,
#ifdef _WIN32+#else足够简洁 - 如果要给 Linux 单独加
epoll、/proc访问或特定头文件(如<sys></sys>),就必须显式写#elif defined(__linux__) - 顺序必须是
_WIN32最前:某些 MinGW 环境下__linux__不定义,但__unix__可能被误设;_WIN32是唯一强信号
示例:
通过 SSH 隧道从 Android 设备在互联网上对 Windows PC 实现完整远程控制的设置与使用指南,适用于远程桌面访问、安全 RDP 等。
#ifdef _WIN32 #include <windows.h> std::string port = "COM1"; #elif defined(__linux__) #include <sys> std::string port = "/dev/ttyUSB3"; #elif defined(__APPLE__) #include <targetconditionals.h> #if TARGET_OS_MAC std::string port = "/dev/cu.usbserial"; #endif #else #error "Unsupported platform" #endif</targetconditionals.h></sys></windows.h>
Android 和 WSL 怎么办?
它们都会触发 __linux__,但行为和 libc 差异大,必须额外排除:
- Android 同时定义
__linux__和__ANDROID__,应写成:#if defined(__linux__) && !defined(__ANDROID__)区分桌面 Linux - WSL 使用 Linux 内核+Linux 工具链,只定义
__linux__;但若你在 Windows 上用 MinGW 编译,就只定义_WIN32——两者不会共存,无需特殊处理 - 别试图用
getenv("OS")或system("uname -s"):前者在 Linux/macOS 下返回nullptr,后者依赖 shell、可能被注入、无法用于静态库或 freestanding 环境
CMake 中要不要用 add_compile_definitions 手动加宏?
没必要。现代编译器已经为你定义好了 _WIN32 和 __linux__,手动加反而容易覆盖或冲突。CMake 的 add_compile_definitions 更适合定义项目级业务宏(如 ENABLE_LOGGING),而不是操作系统判断。
真正该用 CMake 控制的是源文件选择或链接逻辑:
if(WIN32) target_sources(app PRIVATE src/platform_win.cpp) elseif(UNIX AND NOT APPLE) target_sources(app PRIVATE src/platform_linux.cpp) endif()
这种写法比满屏 #ifdef 更易维护,也避免宏嵌套过深带来的可读性问题。
最容易被忽略的一点:iOS 和 Android 都需前置判断——__ANDROID__ 必须放在 __linux__ 前面,TARGET_OS_IPHONE 必须在 #include <targetconditionals.h></targetconditionals.h> 之后才能用,否则预处理器直接报错。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










