【问题标题】:Detecting 64bit compile in C在 C 中检测 64 位编译
【发布时间】:2011-07-13 11:16:55
【问题描述】:

是否有 C 宏或某种方法可以检查我的 c 程序在 C 编译时是编译为 64 位还是 32 位?

编译器:GCC 我需要检查的操作系统:Unix/Linux

另外,如果操作系统支持 64 位,我如何检查运行程序时的情况?

【问题讨论】:

  • 看看following question。它概述了__LP__ gcc 预处理器指令的使用
  • 你为什么想知道?
  • 您要检查二进制可执行文件并确定用于创建该文件的编译器选项吗?
  • 等一下...你的意思是你已经有了二进制文件然后想要检查它? (因为您提到“已编译”)或编译时(因为您提到 C 宏)?
  • @Daniel:我明白你想做什么,问题只是为什么。您的问题并不完全有效,因为“64 位架构”不是一个定义非常明确的术语(您是否想要 64 位寄存器、64 位数据总线、64 位指针),您的编程是否仅适用于 x86或便携式...

标签: c linux unix gcc


【解决方案1】:

既然你标记了这个“gcc”,试试

#if __x86_64__
/* 64-bit */
#endif

【讨论】:

  • 另一个要测试的宏是'_____LP64_____',它将在非 x86-64 架构上工作。
  • 测试任何以_[A-Z]__ 开头的宏几乎肯定是错误的答案。
  • @R..:不,这几乎肯定是正确的答案。以_[A-Z]__ 开头的宏由实现(即编译器/预处理器)保留,这意味着您不能自己定义它们,但您当然可以测试它们的存在以查询实现。
  • @Adam:结果只对某些实现有意义。如果您改为测试像UINTPTR_MAX 这样的标准 宏,它在所有实现中都是可靠的。 (提示:一个有效的实现可以愉快地在 32 位机器上预定义 __LP64__,或者作为一个更极端的例子,它可以将 all__ 开头的宏名称视为已定义,除非它们是明确未定义。)
  • @R..:OTOH,C99 标准保证 uintptr_t 大到足以容纳一个指针,但它不保证它不会比需要的大。即使所有指针都是 32 位,实现也可以使用 64 位 uintptr_t。或者,就此而言,由于 uintptr_t 在 C99 中是可选的,因此您的“标准”宏可能无论如何都不会被定义。
【解决方案2】:

Use a compiler-specific macro.

我不知道你的目标是什么架构,但由于你没有指定它,我会假设普通的英特尔机器,所以你很可能有兴趣测试 Intel x86 和 @ 987654323@.

例如:

#if defined(__i386__)
// IA-32
#elif defined(__x86_64__)
// AMD64
#else
# error Unsupported architecture
#endif

但是,我更喜欢将它们放在单独的头文件中并定义我自己的编译器中性宏。

【讨论】:

  • 使用标准宏(见我的回答),而不是编译器特定的。
  • @R.. 是的,我知道那个,它与 C++ 代码中断,所以我通常坚持使用特定于编译器的代码。
  • 然后使用ULONG_MAX 而不是UINTPTR_MAX。在任何现实世界的 unixy 系统上,它们都是相同的。假设long 和指针大小相同,肯定比假设存在某些特定编译器的宏更容易移植。
  • @R.. 在 64 位 Windows 上仍然是错误的。我更喜欢我的代码编译失败,而不是默默地编译错误的东西。
  • 我投反对票。原因见我的回答。一般来说,这些场景都不能可靠地表明 64 位地址空间和非模拟 64 位算术是否可用,因此它们基本上是无用的,除非在构建系统的上下文中不是不可知论者。因此,最好设置构建宏,以便构建系统可以选择编译哪个变体。
【解决方案3】:

相同的程序源可以(并且应该能够)在 64 位计算机、32 位计算机、36 位计算机中编译……

因此,仅通过查看源代码,如果它有任何好处,您无法判断它将如何编译。如果源不是那么好,可能会猜到程序员假设将用于编译它。

我的回答是:

有一种方法可以检查源文件所需的位数仅适用于不良程序

无论编译多少位,您都应该努力使您的程序正常工作。

【讨论】:

  • 如果你需要使用内联汇编,你必须使用特定于架构的宏。
  • 如果你需要使用内联汇编,知道位数是没有用的。您需要知道拱门的名称并调整您的构建系统/宏/等。相应地。
  • @R..:嗯,通常位数就足够了。尤其是如果您知道您的应用程序专门用于 x86 硬件,那么您通常只需要知道编译器是 32 位还是 64 位,就可以编写正确的汇编源代码。
  • @deltamind106:你真的还在 2015 年生产仅 x86 的产品吗?您预计该业务线会存在多长时间? :-)
  • 我赞成。我的回答更详细地说明了为什么您的回答是正确的。
【解决方案4】:

这是不假设 x86 或其他任何东西的正确且可移植的测试:

#include <stdint.h>
#if UINTPTR_MAX == 0xffffffff
/* 32-bit */
#elif UINTPTR_MAX == 0xffffffffffffffff
/* 64-bit */
#else
/* wtf */
#endif

【讨论】:

  • 我知道这个问题是针对 C 的,但由于它经常与 C++ 混合(或包含在其中),所以这里有一个 C++ 警告:C99 要求获取 C++ 中定义的限制宏,您必须在包含标题之前定义__STDC_LIMIT_MACROS。由于它可能已经包含在内,因此确保正确定义的唯一方法是强制客户端始终将其作为第一个标头包含在源文件中,或者将 -D__STDC_LIMIT_MACROS 添加到所有文件的编译选项中。
  • 可移植性在理论上受到uintptr_t 是可选类型这一事实的限制。我怀疑如果 64 位实现省略它是不恰当的,因为 unsigned long long 是一个足够大的整数类型。
  • 我的观点是,一个忽略uintptr_t 的系统可能有很好的理由这样做(例如,一个非常病态的或至少非典型的记忆模型),并且任何基于以下假设的假设这是“32 位系统”或“64 位系统”在这样的实现上是无效的。因此,我的答案中的“wtf”案例可能应该包含#error,或者完全不依赖于关于内存模型、类型大小等的传统假设的超可移植代码。
  • 这不适用于 Linux PAE 内核。激活 PAE 的内核是 32 位的,但可以像 64 位系统一样寻址 RAM。此代码通过检查最大可寻址 RAM 来确定架构。 32 位 PAE 内核机器将被视为 64 位,因此插入的源代码(可能是一些内联汇编指令)将不起作用。
  • 从我的角度来看,任何可以原生执行 64 位算术的架构都是 64 位架构。并且有几种架构只有 24 位地址总线,但仍称为“32 位”,因为它们的寄存器是 32 位的。 8 位 MCU 也是如此,尽管它们的地址总线通​​常为 14 到 16 位或更多
【解决方案5】:

一个简单的,会让语言律师紧张。

if(sizeof (void *) * CHARBIT == 64) {
...
}
else {
...
}

由于它是一个常量表达式,优化编译器将放弃测试,只将正确的代码放入可执行文件中。

【讨论】:

  • 这通常是正确的,但是请不要再做出诸如“......所以优化编译器将......”之类的断言。预处理器就是预处理器,当条件为真时,往往“else”后面的代码不会编译。
  • 我看不出预处理器有什么关系? OP 要求一种方法来检测所使用的 mem 模型(64 位或 32 位),他没有要求预处理器解决方案。没有人问过替代条件编译的方法。当然,我的解决方案要求两个分支在语法上都是正确的。编译器将始终编译它们。如果编译器正在优化,它将删除生成的代码,但即使没有,也没有问题。想详细说明你的意思吗?
  • 好的,你是对的。确切的措辞是“C 宏或某种方式”。一开始我并没有注意到“某种方式”。
  • @ShelbyMooreIII:嗯嗯……对不起? 32 位与 64 位目标的区别与 int 的大小完全无关(实际上,它的大小不同,例如在 Linux/BSD 和 LLP64 中使用的 LP64在 Windows 中使用,而两者都是非常明显的 64 位)。它也与编译器优化特定操作的速度(或 Javascript 的执行速度)无关。
  • 不检测 64 位架构上的 ILP32 ABI,例如the Linux x32 ABIAArch64 ILP32 ABI。那是 64 位模式下的 32 位指针。所以long long 在这些目标上仍然有效,不像在 32 位 CPU 上,64 位整数每次操作需要 2 条指令和 2 个寄存器。
【解决方案6】:

这个问题是模棱两可的,因为它没有指定要求是针对 64 位指针还是 64-bit native integer arithmetic,或两者兼而有之。

其他一些答案表明如何检测 64 位指针。尽管问题字面上规定“编译为”,但请注意,这并不能保证 64 位地址空间可用。

对于许多系统,检测 64 位指针等同于检测到 64 位算术未被模拟,但不能保证所有潜在情况都如此。例如,虽然 Emscripten 使用具有 maximum size of 232-1 的 Javascript 数组来模拟内存,以提供编译针对 64 位的 C/C++ 代码的兼容性,但我相信 Emscripten 是 agnostic about the limits(尽管我没有对此进行测试)。然而,不管编译器规定的限制如何,Emscripten 总是uses 32-bit arithmetic。因此,Emscripten 似乎会采用针对 64 位 int 和 64 位指针的 LLVM 字节码,并尽 Javascript 的能力模拟它们。

我最初提议如下检测 64 位“本机”整数,但正如 Patrick Schlüter 指出的那样,这只检测到 rare case of ILP64

#include <stdint.h>
#if UINT_MAX >= 0xffffffffffffffff
// 64-bit "native" integers
#endif

因此,正确的答案是,通常您不应该根据编译器报告的限制值对模糊的“64 位”分类的地址空间或算术效率做出任何假设。您的编译器可能支持特定 data modelmicroprocessor architecture 的不可移植预处理器标志,但考虑到问题针对 GCC 和 Emscripten 场景(其中 Clang 模拟 GCC),即使这些可能会产生误导(尽管我还没有测试过) )。

一般来说,这些场景中的任何一个都不能可靠地指示64 位地址空间和非模拟 64 位算术是否可用,因此它们基本上是无用的( wrt to 所说的属性)除了在不可知的构建系统的上下文中。因此对于所说的属性,最好设置构建宏,以便构建系统可以选择编译哪个变体。

【讨论】:

  • 除了经典的 Crays 和已失效的 HAL 之外,没有人使用 ILP64(即使 Cray 也是 SILP64)。所以试图找出 int 算术是否是 64 位并没有太大的预测价值。
  • @PatrickSchlüter 你是对的,32 位 int 不保证 uint64_t 算术是用 32 位算术模拟的。我会更正我的答案。
【解决方案7】:

GLIBC 本身使用这个(在inttypes.h):

#if __WORDSIZE == 64

【讨论】:

    【解决方案8】:

    使用此 UINTPTR_MAX 值检查构建类型。

    #include <stdio.h>
    #include <limits.h>
    
    #if UINTPTR_MAX == 0xffffffffffffffffULL               
    # define BUILD_64   1
    #endif
    
    int main(void) {
    
        #ifdef BUILD_64
        printf("Your Build is 64-bit\n");
    
        #else
        printf("Your Build is 32-bit\n");
    
        #endif
        return 0;
    }
    

    【讨论】:

      【解决方案9】:

      编译器和平台中立的解决方案是这样的:

      // C
      #include <stdint.h>
      
      // C++
      #include <cstdint>
      
      #if INTPTR_MAX == INT64_MAX
      // 64-bit
      #elif INTPTR_MAX == INT32_MAX
      // 32-bit
      #else
      #error Unknown pointer size or missing size macros!
      #endif
      

      避免使用以下划线开头的宏。它们不是标准的,可能在您的编译器/平台上丢失。

      【讨论】:

      • 这实际上是最佳实践!
      猜你喜欢
      • 2023-03-06
      • 1970-01-01
      • 2010-09-14
      • 2018-05-02
      • 2018-10-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多