应优先用 _msc_ver 判断 msvc 兼容环境(含 clang-cl),再用 __clang__ 判原生 clang,最后用 __gnuc__ 且排除前两者判 gcc;宏顺序决定分支逻辑,clang-cl 下 _msc_ver 优先级高于 __clang__。

直接看 _MSC_VER、__GNUC__、__clang__ 这三个宏就能准确区分 MSVC / GCC / Clang,其他“变体宏”(如 __GNUG__、__INTEL_COMPILER)要么冗余,要么不可靠,不建议作为主判断依据。
怎么用 _MSC_VER 稳定识别 MSVC 或 clang-cl
_MSC_VER 是唯一跨 VS 版本稳定可比的 MSVC 厂商标识:VS2019 是 1920,VS2022 是 1930,VS2025 是 1940。它在原生 MSVC 和 clang-cl 模式下都会被定义——这不是 bug,而是明确告诉你:“当前 ABI 和标准库行为对齐 MSVC”。
- 用
#ifdef _MSC_VER判断是否处于 MSVC 兼容环境,不要写#if defined(_MSC_VER) && _MSC_VER > 0,冗余且无意义 - 别碰
_MSC_FULL_VER:它含构建号(如 193030705),不同 Update 值跳跃极大,无法用于版本区间判断 - Clang 在 Windows 上用
-fms-compatibility时也会定义_MSC_VER,此时你应按 MSVC 行为走,而非 Clang 行为
为什么 __GNUC__ 不能单独用来识别 GCC 厂商
__GNUC__ 被 GCC、MinGW-GCC、Clang(兼容模式)甚至某些旧版 Intel ICC 都会定义,仅靠它无法区分“真 GCC”和“假装 GCC”的编译器。
- 要确认是 GCC 工具链,必须组合排除:
#if defined(__GNUC__) && !defined(__clang__) && !defined(_MSC_VER) -
__GNUG__是 GCC C++ 前端专属,但值与__GNUC__相同,无额外信息,没必要引入 - MinGW 环境下
__GNUC__和_WIN32共存是常态,不代表“Linux 宏也生效”,别误判平台
__clang__ 是 Clang 的可靠标识,但要注意它的“伪装模式”
__clang__ 只在原生 Clang(macOS 默认、Linux 手动安装、iOS/macOS Xcode 构建)中定义,MSVC 和 GCC 永远不会定义它——这点比 __GNUC__ 干净得多。
- Clang 在 Windows 上用
clang-cl时,__clang__仍被定义,但_MSC_VER也被定义;此时应优先按_MSC_VER分支处理,因为 ABI 和头文件路径已切换为 MSVC 模式 - 别用
__llvm__:它是 LLVM 后端标识,Clang、dragonegg、llc 都可能定义,完全不能代表 Clang 厂商 - Clang 版本判断应结合
__clang_major__、__clang_minor__,而不是依赖__GNUC__的值(Clang 会模仿 GCC 版本号,但语义不一致)
Android、iOS、macOS 等平台宏不是编译器厂商标识,别混用
__ANDROID__、TARGET_OS_IPHONE、__APPLE__ 描述的是目标平台,不是编译器。它们可能和某个编译器强绑定(如 iOS 只用 Clang),但逻辑上属于不同维度。
- 在 Android NDK 构建中,你可能同时看到
__clang__(编译器)、__ANDROID__(平台)、__linux__(内核),三者共存,各自负责不同判断 - 错误写法:
#elif __clang__下直接写 iOS 专用 API 调用——Clang 也能编译 Linux 代码,必须再套一层#ifdef __APPLE__ - CMake 中若用
target_compile_definitions(mylib PRIVATE CLANG_BUILD)手动注入宏,会破坏预处理器的真实厂商判断,慎用
最易忽略的一点:宏判断顺序决定行为。比如先写 #ifdef __clang__ 再写 #ifdef _MSC_VER,在 clang-cl 下就会进错分支——因为两个宏都定义了,而 __clang__ 出现在前面。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











