如何使用预处理指令检查操作系统?

309

我希望我的代码能够根据编译所在的操作系统执行不同的功能。我正在寻找类似于以下代码:

#ifdef OSisWindows
// do Windows-specific stuff
#else
// do Unix-specific stuff
#endif

有没有一种方法可以做到这一点?是否有更好的方法来完成同样的事情?


11
抱歉,这个问题已经在多年前被提出了。 - John_West
这是关于 C 而不是 C++ - ilgaar
如何在C预处理器中可靠地检测Mac OS X、iOS、Linux和Windows? 如何在C、C++中检测Windows或Linux? - phuclv
@CoryKlein 不,那个问题是这个问题的重复。 - Akib Azmain Turja
请参阅CMAKE中特定于操作系统的指令(CMAKE_SYSTEM_NAME)。 - milahu
显示剩余2条评论
17个回答

446

操作系统预定义宏网站提供了一个非常完整的检查列表。以下是其中一些检查项,附带它们的链接:

Windows

_WIN32   32位和64位
_WIN64   仅64位
__CYGWIN__

Unix(Linux、*BSD,但不包括Mac OS X)

请参阅这个相关问题,了解使用此检查时可能会遇到的一些问题。

unix
__unix
__unix__

Mac OS X

__APPLE__ 也用于旧版系统
__MACH__

两者都定义了;检查任何一个都应该可以工作。

Linux

__linux__
linux 已过时(不符合POSIX标准)
__linux 已过时(不符合POSIX标准)

FreeBSD

__FreeBSD__

Android

__ANDROID__


2
该网站未包含iOS,因此无法区分iOS和OS X。 - Gary Makin
4
Mac OS没有定义__unix__。你为什么要把它包含在列表中呢? - Victor Sergienko
3
cpp -dM /dev/null 可以列出您安装的gcc版本中所有预定义的宏。 - katta
3
请注意,Cygwin定义了unix符号,但未定义win32符号,因此请小心。另一方面,它确实定义了__CYGWIN__ - David Given
显示剩余4条评论

85

显示Windows上的GCC定义:

gcc -dM -E - <NUL:

在Linux上:

gcc -dM -E - </dev/null

MinGW中的预定义宏:

WIN32 _WIN32 __WIN32 __WIN32__ __MINGW32__ WINNT __WINNT __WINNT__ _X86_ i386 __i386

在 UNIX 系统上:

unix __unix__ __unix

1
Windows和Unix并不是唯一的操作系统。 - phuclv
这很方便,但请注意,在 macOS 上不支持 unix__unix____unix,只定义了 __APPLE____MACH__ - mklement0
在Linux上,使用gcc命令时,会显示error: unrecognized command-line option ‘-q’,这是因为在gcc -dM -E - </dev/null命令中使用了不被识别的选项-q - undefined

65

根据nadeausoftwareLambda Fairy的回答

#include <stdio.h>

/**
 * Determination a platform of an operation system
 * Fully supported supported only GNU GCC/G++, partially on Clang/LLVM
 */

#if defined(_WIN32)
    #define PLATFORM_NAME "windows" // Windows
#elif defined(_WIN64)
    #define PLATFORM_NAME "windows" // Windows
#elif defined(__CYGWIN__) && !defined(_WIN32)
    #define PLATFORM_NAME "windows" // Windows (Cygwin POSIX under Microsoft Window)
#elif defined(__ANDROID__)
    #define PLATFORM_NAME "android" // Android (implies Linux, so it must come first)
#elif defined(__linux__)
    #define PLATFORM_NAME "linux" // Debian, Ubuntu, Gentoo, Fedora, openSUSE, RedHat, Centos and other
#elif defined(__unix__) || !defined(__APPLE__) && defined(__MACH__)
    #include <sys/param.h>
    #if defined(BSD)
        #define PLATFORM_NAME "bsd" // FreeBSD, NetBSD, OpenBSD, DragonFly BSD
    #endif
#elif defined(__hpux)
    #define PLATFORM_NAME "hp-ux" // HP-UX
#elif defined(_AIX)
    #define PLATFORM_NAME "aix" // IBM AIX
#elif defined(__APPLE__) && defined(__MACH__) // Apple OSX and iOS (Darwin)
    #include <TargetConditionals.h>
    #if TARGET_IPHONE_SIMULATOR == 1
        #define PLATFORM_NAME "ios" // Apple iOS
    #elif TARGET_OS_IPHONE == 1
        #define PLATFORM_NAME "ios" // Apple iOS
    #elif TARGET_OS_MAC == 1
        #define PLATFORM_NAME "osx" // Apple OSX
    #endif
#elif defined(__sun) && defined(__SVR4)
    #define PLATFORM_NAME "solaris" // Oracle Solaris, Open Indiana
#else
    #define PLATFORM_NAME NULL
#endif

// Return a name of platform, if determined, otherwise - an empty string
const char *get_platform_name() {
    return (PLATFORM_NAME == NULL) ? "" : PLATFORM_NAME;
}

int main(int argc, char *argv[]) {
    puts(get_platform_name());
    return 0;
}

已在以下平台上使用GCC和clang进行测试:

  • Debian 8
  • Windows(MinGW)
  • Windows(Cygwin)

亲爱的 @MD XF,请说明您的Windows、MinGW和Cygwin版本。 - PADYMKO
Windows 7企业版6.1.7601。Cygwin 2.7.0-1。我找不到MinGW版本,但昨天已经下载了它。 - MD XF
你可能应该知道 - 这个程序是标准的 C,所以它应该在所有兼容的系统上都能运行。 - MD XF
亲爱的 @MD XF,感谢您提供这些信息。我已将您添加为此答案的贡献者。 - PADYMKO
1
这样 _WIN64 就永远无法被定义,因为 _WIN32 已经被定义了。 - not2qubit

9

Microsoft C/C++编译器(MSVC)预定义宏可以在此处找到

我认为您正在寻找以下内容:

  • _WIN32 - 当编译目标为32位ARM、64位ARM、x86或x64时,定义为1。否则未定义。
  • _WIN64 - 当编译目标为64位ARM或x64时,定义为1。否则未定义。

gcc编译器预定义宏可以在此处找到

我认为您正在寻找以下内容:

  • __GNUC__
  • __GNUC_MINOR__
  • __GNUC_PATCHLEVEL__

谷歌搜索适合您的编译器预定义内容。


9
在MinGW上,_WIN32定义检查不起作用。这里有一个解决方案:
#if defined(_WIN32) || defined(__CYGWIN__)
    // Windows (x86 or x64)
    // ...
#elif defined(__linux__)
    // Linux
    // ...
#elif defined(__APPLE__) && defined(__MACH__)
    // Mac OS
    // ...
#elif defined(unix) || defined(__unix__) || defined(__unix)
    // Unix like OS
    // ...
#else
    #error Unknown environment!
#endif

请查看以下链接获取更多信息: https://sourceforge.net/p/predef/wiki/OperatingSystems/


这个链接提供了有关操作系统的预定义宏的信息。

最佳答案是因为它涵盖了Cygwin。 - mercury

8

在大多数情况下,最好检查给定的功能是否存在。例如:检查函数pipe()是否存在。


3
有没有一种简单的方法来检查一个函数是否已定义? - hayalci
1
如果您正在使用自动配置,则可以使用AC_CHECK_FUNCS()检查函数。 如果函数可用,则AC_CHECK_FUNCS(pipe sqrt)将定义HAVE_PIPE和HAVE_SQRT。 我不知道其他构建工具的情况,但我认为它们也以某种方式支持此功能。 - quinmars
从C++17开始,有__has_include。我认为它在C中尚未标准化,但所有主要编译器(GCC、Clang、ICC、MSVC)都将其作为供应商特定的扩展实现,即使在C模式下也是如此。 - Alcaro

8
#ifdef _WIN32
// do something for windows like include <windows.h>
#elif defined __unix__
// do something for unix like include <unistd.h>
#elif defined __APPLE__
// do something for mac
#endif

4
没有按照C标准设置的标准宏。一些C编译器会在某些平台上设置一个宏(例如,苹果的修补过的GCC会设置一个宏以指示它正在编译苹果系统和达尔文平台)。您的平台和/或C编译器也可能设置了一些内容,但没有通用的方法。
像hayalci所说,最好在构建过程中设置这些宏。大多数编译器很容易定义宏而不修改代码。您可以简单地将-D MACRO传递给GCC。
gcc -D Windows
gcc -D UNIX

而在你的代码中:

#if defined(Windows)
// do some cool Windows stuff
#elif defined(UNIX)
// do some cool Unix stuff
#else
#    error Unsupported operating system
#endif

3
您可以使用Boost.Predef,其中包含各种针对目标平台的预定义宏,包括操作系统(BOOST_OS_*)。是的,Boost通常被认为是一个C++库,但这个库是一个预处理器头文件,也适用于C语言!

该库根据C、C++、Objective C和Objective C++预定义的宏或通用可用头文件中定义的宏收集一组编译器、架构、操作系统、库和其他版本号。这个库的想法源于扩展Boost Config库以提供比其支持的功能定义更多、更一致信息的建议。以下是该简短提案的编辑版本。

例如:
#include <boost/predef.h>
// or just include the necessary header
// #include <boost/predef/os.h>

#if   BOOST_OS_WINDOWS
#elif BOOST_OS_ANDROID
#elif BOOST_OS_LINUX
#elif BOOST_OS_BSD
#elif BOOST_OS_AIX
#elif BOOST_OS_HAIKU
...
#endif

完整列表可以在BOOST_OS操作系统宏中找到。

在 Godbolt 上的演示

另请参阅如何从boost获取平台ID?


3

2
那篇元帖已被删除。有趣的是,一篇关于因版主原因被删除的帖子的元帖也因版主原因被删除了。 - MD XF
没错,绝对疯狂。 - Konstantin Burlachenko

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接