使用C/C++代码识别平台是否为Linux或Windows。

11

我在面试中被问到如何通过可运行的C代码识别平台?

该代码必须能够在两个平台上运行(即不能使用特殊的头文件函数进行检查)。


1
像这样吗?https://dev59.com/jW025IYBdhLWcg3wl3Am - chris
2
这些问题涉及宏,但我认为OP真正想知道的是如何使用可在多个平台上编译的代码来实现。我建议探测文件系统以查看文件的存在情况,从而确定操作系统。 - Paul Dixon
1
从概念上讲,解决这个问题可能更有意义的是在构建环境层面而不是语言层面上进行。也就是说,让构建环境定义一个预处理器标志,允许代码确定平台。否则,你会将关于编译器的知识引入到源代码中,这会限制可移植性并且总体上不太好。 - ComicSansMS
2
我猜这个问题不是关于编译器指令的,因为它们不是可运行的 C 代码,对吧?更像是我要使用什么代码来检测平台。有点奇怪,因为你必须针对特定平台编译 C 代码。对我来说,这个问题有点模糊不清。 - Philip Stuyck
2
这个问题怎么会被标记为重复的呢?它是完全不同的问题。(也许是一个无意义的问题,或者是一个没有答案的问题,但它肯定不是与之链接的那个重复问题。) - James Kanze
7个回答

7
一个快速的谷歌搜索会显示这个。
#if defined(_WIN64)
    /* Microsoft Windows (64-bit). ------------------------------ */

#elif defined(_WIN32)
    /* Microsoft Windows (32-bit). ------------------------------ */

#endif

操作系统识别宏是由所有C/C++编译器预定义的,以启用#if/#endif集来包装特定于操作系统的代码。这在必须使用低级库函数进行快速磁盘I/O、进程间通信或线程的跨平台代码中经常是必需的。虽然Windows和其他操作系统之间的差异很明显,但即使在UNIX风格的操作系统中也可能需要#if/#endif结构。本文概述了常见的编译器,并展示了如何使用预定义的宏在编译时检测常见的操作系统。
链接:http://nadeausoftware.com/articles/2012/01/c_c_tip_how_use_compiler_predefined_macros_detect_operating_system

请点击此链接获取更多操作系统特定标志信息:https://users.pja.edu.pl/~jms/qnx/help/watcom/compiler-tools/cpwcc.html + https://github.com/emrainey/Concerto/wiki/Predefined-Macros-for-Code。 - parasrish

6
这个问题很奇怪。您不能提供一个可在多个平台上运行的可执行文件。所以显而易见的答案是:在一个平台上编译它:如果它能运行,那么这就是您所在的平台;如果不能,那么您就在其他平台上。
假设您确实有单独的可执行文件,那么真正的答案其实并不存在。首先,您认为“不同的平台”是什么?Sparc架构下的Linux和PC上的Linux算不算不同的平台?两个不同版本的Linux呢?在CygWin下运行呢?无论您如何定义它,都可能需要为每个平台进行单独的测试。但这个问题很愚蠢,因为除了将不同版本视为不同平台外,您将需要为每个平台准备一个不同的可执行文件,这意味着您必须在编译时进行区分。
仅供参考:首先,我会使用boost::filesystem,并尝试读取目录“c:\\”和“/usr”。理论上,您可以在Unix下的当前目录中有一个名为“c:\\”的目录,或者在Windows下的C盘中有一个名为“/usr”的目录,但这种情况的概率非常小。否则,尝试读取“/proc/myPID”中的各种文件。该目录在Windows下几乎肯定不存在,即使存在,它的动态结构也不同于各种Unix。而且不同的Unix之间的结构也有所不同,因此您甚至应该能够区分Solaris和Linux。
如果只是在PC上比较Windows和Linux,那么最简单的方法当然是以32位模式编译,并获取本地变量的地址。如果它大于0x8000000,则在Linux下;如果小于,则在Windows下。

如果我们只需要在两个平台上都以64位模式编译,那么有没有一种方法可以通过地址区分这两个平台? - Manjunath

3
这有点含糊不清。除非你在Wine下运行(然后参见http://wiki.winehq.org/DeveloperFaq#detect-wine),否则二进制文件不会在两个系统中同时运行。
在编译时,您可以检查特定于Windows或Linux的#define,但可以说这是特定于编译器和支持的标准,因此我不确定是否允许您为了面试问题而这样做(即它可以与您的“不使用特殊头文件函数来检查”限制一起考虑)。
总的来说,在优先进行编译时检查之前,我认为这没有任何意义,而且在Wine下可能无法工作,但是您可以使用dlsym()来检查自己的符号,这些符号已知仅存在于操作系统之一上,或查看自己的可执行映像以查看它是什么。
实际上,您还可以检查环境变量,并根据此作出相当好的判断,尽管某些人可能能够故意欺骗您的程序,使其做出错误的评估,或者您的确定方法可能会在操作系统的不同版本,不同的shell等下失效。

在运行时,您可以执行例如stat("/dev", &buf),这仅适用于Linux。尝试使用popen(3)说uname是另一种可行的方法:如果您在Linux上,则可以读取"linux\n",而类似cygwin的环境显然报告NT也可以在此处查看


提到Wine是一个有趣的问题。在Wine下运行的程序应该报告Windows还是Linux?(同样的问题也适用于在Windows下使用CygWin库编译的程序。) - James Kanze
1
Cygwin不支持反向操作。它是一个源代码兼容层(而非二进制文件),因此代码必须专门为其编译。它不能在Windows上运行Linux二进制文件(就像Wine在Linux上运行Windows二进制文件一样)。 - nobody

2
除了通常的宏调用之外:
#ifdef _WIN32
  ...

#if defined (_MSC_VER)
  ...

#ifdef __unix__
  ...

您可以简单地使用getenv()函数,并测试一些设置的变量,例如SHELLWINDIR。有趣的情况如下:
  • mingw在Windows 32/64下运行
  • cygwin在Windows 32/64下运行
  • wine在Linux 32/64下运行
当然,“Windows”环境变量也可以在Linux中设置-反之亦然。

2

大多数常见的方法在编译时确定。

如果您真的坚持要在运行时确定,您可以尝试使用一种设备(例如,尝试打开一个名为/dev/null的文件应该在Linux上工作,但通常在Windows上失败)。


1
杰瑞,如果存在目录 C:\dev 和文件 C:\dev\null 怎么办?例如,这个命令在这种情况下可以正常工作:notepad /dev/null - user184968
如果PC上的唯一可能性是Windows和Linux,那么最简单的方法(至少在32位模式下)就是获取main函数中一个本地变量的地址。如果它小于0x80000000,则表示你正在使用Windows。如果它大于0x80000000,则表示你正在使用Linux。 - James Kanze
@JerryCoffin 在我看来,这听起来就像是一个很大的“如果”。按照这个规则,Sparc上的Solaris将看起来像Linux;HP-UX将显示为Windows。而我不知道苹果将显示为什么(也许是Linux,因为它是在Intel架构上使用g++编译并运行的Unix系统,但你永远不知道)。 - James Kanze
@JamesKanze:说实话,整个想法对我来说听起来有点靠不住。虽然我尝试着回答了你的问题,但我没有看到任何理性的理由让你在运行时尝试解决这个问题,因为在编译时处理它有很多方法。像Java这样的语言,运行时检测是有意义的,但在C和/或C++基于VM的实现变得普遍之前,整个想法似乎不太明智。 - Jerry Coffin
@JerryCoffin 我完全同意。在两个不同的系统上无法运行相同的二进制文件,因此你必须编译两次。在这一点上,很容易在编译时进行区分。还有一个问题是如何处理这种区别。例如 if ( windows ) LoadLibrary(...); else dlopen(...); 这样的代码不会链接到任何地方。 - James Kanze
显示剩余2条评论

2

如果你只考虑 Linux 平台或 Windows 平台,这段代码将在 C 和 C++ 中按需求工作。

#include <stdio.h>

#if defined(__linux__) // any linux distribution
    #define PLATFORM "linux"
#elif defined(_WIN32) // any windows system
    #define PLATFORM "windows"
#else
    #define PLATFORM "Is not linux or windows"
#endif


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

完整的解答和示例请参见此处https://dev59.com/wHVC5IYBdhLWcg3w7V33#42040445


1

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