最可靠方式是用预处理宏判断操作系统:_WIN32(Windows)、__linux__(Linux)、__APPLE__(macOS/iOS)、BSD系列宏;禁用linux、__unix__等不可靠宏,避免混淆编译器与系统宏。

如何用 __linux__、_WIN32 等宏判断操作系统
直接靠预处理宏是最轻量、最可靠的方式,编译期就能分流逻辑,不依赖运行时 API。主流编译器(GCC、Clang、MSVC)都支持一组约定俗成的宏,但命名和覆盖范围有差异,不能只记一个。
常见组合如下:
-
_WIN32:所有 Windows 平台(包括 64 位),MSVC 和 MinGW 都定义;_WIN64仅在 64 位 Windows 下额外定义 -
__linux__:Linux 内核系统(GCC/Clang 默认定义),注意不是linux或LINUX -
__APPLE__+__MACH__:macOS 和 iOS(二者通常同时存在,单用__APPLE__更稳妥) -
__FreeBSD__、__OpenBSD__、__NetBSD__:各 BSD 变种,名称大小写敏感
别写 #ifdef linux——它几乎从不被定义;也别依赖 __unix__,它在某些嵌入式或旧环境里可能缺失,而 __linux__ 或 _WIN32 更具确定性。
为什么不能只用 __GNUC__ 判断 Linux
__GNUC__ 表示 GNU 编译器,不是操作系统。MinGW 在 Windows 上用 GCC 编译,会定义 __GNUC__ 但不定义 __linux__;反之,Intel ICC 在 Linux 上也不定义 __GNUC__。混淆会导致头文件包含错误或系统调用误用。
立即学习“C++免费学习笔记(深入)”;
典型翻车场景:
- 误把
__GNUC__当 Linux 标志,结果在 MinGW 下尝试open()而非CreateFileA() - 用
__APPLE__但漏掉__MACH__,导致 macOS 上某些 Mach-O 特有逻辑未触发 - 在交叉编译(如用 Linux 主机编译 ARM 嵌入式固件)时,
__linux__可能仍被定义,但目标系统根本没有 glibc
跨平台代码中宏判断的实用写法
优先用操作系统宏,再按需叠加编译器或架构判断。避免嵌套过深,用清晰的分层结构:
#if defined(_WIN32)
#include
#define PATH_SEP '\\'
#elif defined(__linux__)
#include
#define PATH_SEP '/'
#elif defined(__APPLE__)
#include
#define PATH_SEP '/'
#else
#error "Unsupported OS"
#endif
注意点:
- 用
#if defined(...)比#ifdef更安全,可配合逻辑运算符(如#if defined(__linux__) && !defined(__ANDROID__)) - Android 是 Linux 内核,但 libc 是 bionic,需额外检查
__ANDROID__宏 - 如果要区分 MSVC 和 Clang on Windows,再加
#ifdef __clang__或#ifdef _MSC_VER
运行时判断不如编译期宏可靠
有人试图用 uname()(Linux/macOS)或 GetVersionEx()(Windows)做运行时识别,但这引入了额外依赖、权限问题和链接复杂度。更重要的是:同一份二进制无法适配多系统——你不可能让一个 Windows EXE 去调用 uname()。
真正需要运行时识别的场景极少,比如插件系统加载不同平台的动态库。此时应由宿主程序传入平台标识,而非让插件自己探测。
宏判断的边界容易被忽略:它只反映「编译目标平台」,不是「运行平台」。交叉编译或 Wine 环境下,二者可能不一致——这时你要明确设计契约:你的代码究竟面向哪个平台生成。











