确定C++中的32位还是64位

154
我正在寻找一种可靠的方法来确定C++代码是否在32位还是64位编译。我们想出了使用宏的合理解决方案,但我想知道是否有可能存在无法覆盖的情况或者是否有更好的方法。请注意我们正在尝试在跨平台、多编译器环境下完成这项任务。
#if ((ULONG_MAX) == (UINT_MAX))
# define IS32BIT
#else
# define IS64BIT
#endif

#ifdef IS64BIT
DoMy64BitOperation()
#else
DoMy32BitOperation()
#endif
感谢。

9
如果你真的关心你的计算机体系结构的字长,那么不要忽视它既不是32位也不是64位的可能性。你知道,还有16位和128位的计算机体系结构存在。 - alex tingle
64位操作和32位操作有什么区别? - peterchen
2
你真的不应该将这个条件限制在目标平台的字宽上。相反,直接使用相关数据类型的大小来确定要做什么。stdint.h 可能是你的朋友,或者你可能需要开发一些适当的 typedef。 - Phil Miller
这个测试在Visual Studio 2008 SP1上似乎无法工作。它在32位和64位上都卡在“IS64BIT”上。 - Contango
16个回答

120

不幸的是,没有跨平台宏定义在主要编译器中定义32位/64位。我发现最有效的方法是以下方式。

首先,我选择自己的表示法。我更喜欢使用 ENVIRONMENT64 / ENVIRONMENT32。然后,我找出所有主要编译器用于确定是否为64位环境的方法,并使用其来设置我的变量。

// Check windows
#if _WIN32 || _WIN64
#if _WIN64
#define ENVIRONMENT64
#else
#define ENVIRONMENT32
#endif
#endif

// Check GCC
#if __GNUC__
#if __x86_64__ || __ppc64__
#define ENVIRONMENT64
#else
#define ENVIRONMENT32
#endif
#endif

另一种更简单的方法是直接从编译器命令行设置这些变量。


4
除了GCC和VS,还有其他编译器可用。例如,我想到了QNX和GHS(尽管我怀疑QNX与GCC具有类似的构建时定义)。另外,在你的GCC检查中,你忘记了MIPS64和IA64架构。 - Rom
15
@Rom,肯定不止2个编译器和架构。这只是展示如何解决这个问题的示例,而非完整解决方案。 - JaredPar
3
我说“通常”,但“理想情况”可能更为现实。 - Steve Jessop
10
我认为你应该使用“#if defined(WIN32) || defined(_WIN64)”等语句。 - KindDragon
5
#if _WIN32 || _WIN64表示如果编译器为Windows系统,则执行下面的代码;#elif __GNUC__表示如果编译器为GNU编译器,则执行其后的代码;而#else则表示如果以上两个条件都不成立,则执行其后的代码。最后,#error "Missing feature-test macro for 32/64-bit on this compiler."表示在此编译器上缺少适用于32/64位的特性测试宏。 - Davislor
显示剩余6条评论

108
template<int> void DoMyOperationHelper();

template<> void DoMyOperationHelper<4>() 
{
  // do 32-bits operations
}

template<> void DoMyOperationHelper<8>() 
{
  // do 64-bits operations
}

// helper function just to hide clumsy syntax
inline void DoMyOperation() { DoMyOperationHelper<sizeof(size_t)>(); }

int main()
{
  // appropriate function will be selected at compile time 
  DoMyOperation(); 

  return 0;
}

2
如果 size_t 既不是 4 也不是 8,会发生什么? - Jesper
17
@ Jesper,如果你按上面的示例进行操作,你会得到链接错误。或者你可以为那种情况实现DoMyOperation函数。 - Kirill V. Lyadvinsky
1
巧妙地使用模板,以及对测试重要内容(某些特定类型的大小)而非相关内容的赞扬。 - Phil Miller
2
使用size_t时要小心。例如,在具有多个指针大小的平台上,它可能与指针大小不对应,从而导致问题。 - Logan Capaldo
9
标准规定 size_t 的大小足以容纳系统中分配的任何对象的大小。通常情况下,在条件编译时这是您想要了解的信息。如果这不是您想要的,您可以使用此代码片段,将其替换为其他类型,例如 void* - Kirill V. Lyadvinsky

49

很遗憾,在跨平台、跨编译器的环境下,没有一种单一可靠的方法可以纯粹地在编译时完成这个任务。

  • 如果项目设置有缺陷或损坏(特别是在Visual Studio 2008 SP1上),_WIN32和_WIN64有时可能都未定义。
  • 由于项目配置错误,“Win32”标记的项目可能被设置为64位。
  • 在Visual Studio 2008 SP1上,有时智能感知不会根据当前的#define正确地将代码的正确部分变灰。这使得很难看到确切使用了哪个#define。

因此,唯一可靠的方法是结合三个简单的检查

  • 1)编译时设置,以及;
  • 2)运行时检查,以及;
  • 3)强大的编译时检查

简单检查1/3:编译时设置

选择任何方法来设置所需的#define变量。我建议使用@JaredPar的方法:

// Check windows
#if _WIN32 || _WIN64
   #if _WIN64
     #define ENV64BIT
  #else
    #define ENV32BIT
  #endif
#endif

// Check GCC
#if __GNUC__
  #if __x86_64__ || __ppc64__
    #define ENV64BIT
  #else
    #define ENV32BIT
  #endif
#endif

简单检查2/3:运行时检查

在main()函数中,双重检查确保sizeof()函数的返回值正确:

#if defined(ENV64BIT)
    if (sizeof(void*) != 8)
    {
        wprintf(L"ENV64BIT: Error: pointer should be 8 bytes. Exiting.");
        exit(0);
    }
    wprintf(L"Diagnostics: we are running in 64-bit mode.\n");
#elif defined (ENV32BIT)
    if (sizeof(void*) != 4)
    {
        wprintf(L"ENV32BIT: Error: pointer should be 4 bytes. Exiting.");
        exit(0);
    }
    wprintf(L"Diagnostics: we are running in 32-bit mode.\n");
#else
    #error "Must define either ENV32BIT or ENV64BIT".
#endif

简单检查 3/3: 强大的编译时检查

一般规则是“每个 #define 必须以生成错误的 #else 结尾”。

#if defined(ENV64BIT)
    // 64-bit code here.
#elif defined (ENV32BIT)
    // 32-bit code here.
#else
    // INCREASE ROBUSTNESS. ALWAYS THROW AN ERROR ON THE ELSE.
    // - What if I made a typo and checked for ENV6BIT instead of ENV64BIT?
    // - What if both ENV64BIT and ENV32BIT are not defined?
    // - What if project is corrupted, and _WIN64 and _WIN32 are not defined?
    // - What if I didn't include the required header file?
    // - What if I checked for _WIN32 first instead of second?
    //   (in Windows, both are defined in 64-bit, so this will break codebase)
    // - What if the code has just been ported to a different OS?
    // - What if there is an unknown unknown, not mentioned in this list so far?
    // I'm only human, and the mistakes above would break the *entire* codebase.
    #error "Must define either ENV32BIT or ENV64BIT"
#endif

更新日期 2017-01-17

@AI.G 的评论:

四年之后(不知道以前是否可能),您可以使用静态断言将运行时检查转换为编译时检查:static_assert(sizeof(void*) == 4);。现在,这一切都在编译时完成 :)

附录 A

顺便提一下,可以采用上述规则将整个代码库的可靠性提高:

  • 每个 if() 语句都要以 "else" 结尾,并生成警告或错误。
  • 每个 switch() 语句都要以 "default:" 结尾,并生成警告或错误。

这种方法能很好地起到作用,因为它迫使您预先考虑每种情况,而不是依赖于“else”部分中执行正确代码的(有时是有缺陷的)逻辑。

我使用了这种技术(以及其他许多技术)来编写一个由三万行代码组成的项目,从第一天投入生产以来一直没有出现故障(这是12个月前的事情了)。


sizeof(void*) 是在编译时还是运行时解决的?如果是在编译时解决的,那么在运行时检查总是会是 if(8!=8){...} - Ameen
@ameen 它在运行时解决。此检查的目的是确保如果位数不符合预期,则程序将以适当的错误退出。这意味着开发人员可以立即修复此错误,而不是试图诊断后来出现的微妙错误。 - Contango
3
4年后(不确定以前是否可能),您可以使用静态断言将运行时检查转换为编译时检查:static_assert(sizeof(void*) == 4);。现在所有操作都在编译时完成 :) - Al.G.
@AI.G 评论非常好,我更新了答案并加入了它。谢谢! - Contango
1
static_assert(sizeof(void*) * CHAR_BIT == 32) 更加表达清晰且技术上更加正确(尽管我不知道有哪种字节具有不同于8位的位数的架构)。 - Xeverous
1
请参见我在Stack Overflow上的答案,结合Fluent C++的“更好的宏,更好的标志”(Better Macros, Better Flags),以获取更多信息。链接:https://dev59.com/JXI_5IYBdhLWcg3wJfkM#61063780 - metal

46

你应该能够使用在stdint.h中定义的宏。特别地,INTPTR_MAX正好是你需要的值。

#include <cstdint>
#if INTPTR_MAX == INT32_MAX
    #define THIS_IS_32_BIT_ENVIRONMENT
#elif INTPTR_MAX == INT64_MAX
    #define THIS_IS_64_BIT_ENVIRONMENT
#else
    #error "Environment not 32 or 64-bit."
#endif

微软的某些(全部?)编译器版本不包含 stdint.h。不确定为什么,因为它是一个标准文件。这里有一个可用的版本:http://msinttypes.googlecode.com/svn/trunk/stdint.h


4
为什么微软没有stdint.h呢?因为它是在C99标准中引入的,而微软似乎非常抵制实现甚至来自C99的最简单的东西。即使是不需要编译器更改的简单库文件也是如此,即使是在为C ++编译时已经执行的操作(如语句后声明)。 我知道它需要测试等等,但我也知道微软从Dinkumware / Plauger那里获取了相当大部分的库,而Dinkumware的C99库已经存在多年。 - Michael Burr
2
VC++2010(beta1)有<stdint.h><cstdint>。目前的情况是,VC++库源于Dinkumware(现在仍然如此——TR1也是从那里取出的),但据我所记,在VCBlog上阅读到的内容是它正在进行相当重要的重构工作,以便与/clr编译干净、与所有MSVC非标准类型(如__int64)兼容等等,这就是为什么它不像只需将其放入下一个编译器版本那样简单。 - Pavel Minaev
3
这让我得出了正确的答案,但我认为你应该将其与UINT64_MAX进行比较,而不是INT64_MAX。我使用的是SIZE_MAX == UINT64_MAX -- 可能相同。 - Arno Duvenhage
2
@ArnoDuvenhage:intptr_t保证是有符号类型,所以这个答案是正确的。你的方法也可以,如果我要对指针进行任何棘手的操作(除了重新进行符号扩展以使x86-64指针成为规范化之外),我会选择无符号类型,因为我认为指针是无符号整数。 - Peter Cordes

15

首先,在Windows上这样做行不通。在32位或64位的Windows下,longs和ints都是32位的。我认为检查指针大小是否为8字节可能是更可靠的方法。


3
很不幸,在#if指令中禁止使用sizeof(如果你想一下,预处理器无法判断)。 - EFraim
没错,这就是为什么我建议检查指针的大小而不是使用sizeof - 我想不出一种可移植的方法来做到这一点... - mattnewport
3
问题目前还没有明确要求必须在预处理器时间完成。许多/大多数启用优化的编译器会在运行时消除死代码,即使你像 sizeof(void*) == 8 ? Do64Bit() : Do32Bit(); 这样使用一个测试将其留到运行时。这可能仍然会在二进制文件中留下未使用的函数,但该表达式很可能只被编译为对“正确”函数的调用。 - Steve Jessop
2
@onebyone 解决了函数调用的问题,但如果我想根据平台声明不同类型的变量,那就需要在预处理器中完成,除非你想声明多个变量并根据 if 语句使用它们(如果它们未被使用,也会被优化掉,但代码中不太美观)。 - Falaina
1
那么你是正确的,在条件语句中使用常量表达式不好。但是Kirill的方法可以实现你想要的:template<int> struct Thing; template<> struct Thing<4> { typedef uint32_t type; }; template<> struct Thing<8> { typedef uint64_t type; }; typedef Thing<sizeof(void*)>::type thingtype; - Steve Jessop

11
你可以这样做:
#if __WORDSIZE == 64
char *size = "64bits";
#else
char *size = "32bits";
#endif

2
在许多64位机器上的C和C衍生语言的编程环境中,“int”变量仍然是32位宽,但长整型和指针是64位宽。这些被描述为具有LP64数据模型。 http://www.unix.org/version2/whatsnew/lp64_wp.html - Hermes

8
Try this:
#ifdef _WIN64
// 64 bit code
#elif _WIN32
// 32 bit code
#else
   if(sizeof(void*)==4)

       // 32 bit code
   else 

       // 64 bit code   
#endif

7
这段代码不正确。在64位操作系统上,_WIN32和_WIN64都被定义了。如果你反过来检查(先检查_WIN64),那当然就能正常工作了。 - BertR

6

以下代码在大多数当前环境下工作正常:

  #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) &&     !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
    #define IS64BIT 1
 #else
    #define IS32BIT 1
#endif

6
请注意,使用_WIN64需要先包含<windows.h>。在Visual C++中,最好使用内置的编译器定义:_M_IX86_M_X64_M_ARM_M_ARM64等。 - Chuck Walbourn
对于 PowerPC,我认为您需要检查 __ppc64____powerpc64___ARCH_PPC64。这也适用于 AIX 和其他平台。 - jww

4

借鉴Contango优秀回答,结合Fluent C++的"更好的宏和更好的标志",你可以这样做:

// Macro for checking bitness (safer macros borrowed from 
// https://www.fluentcpp.com/2019/05/28/better-macros-better-flags/)
#define MYPROJ_IS_BITNESS( X ) MYPROJ_IS_BITNESS_PRIVATE_DEFINITION_##X()

// Bitness checks borrowed from https://dev59.com/JXI_5IYBdhLWcg3wJfkM#12338526
#if _WIN64 || ( __GNUC__ && __x86_64__ )
#    define MYPROJ_IS_BITNESS_PRIVATE_DEFINITION_64() 1
#    define MYPROJ_IS_BITNESS_PRIVATE_DEFINITION_32() 0
#    define MYPROJ_IF_64_BIT_ELSE( x64, x86 ) (x64)
    static_assert( sizeof( void* ) == 8, "Pointer size is unexpected for this bitness" );
#elif _WIN32 || __GNUC__
#    define MYPROJ_IS_BITNESS_PRIVATE_DEFINITION_64() 0
#    define MYPROJ_IS_BITNESS_PRIVATE_DEFINITION_32() 1
#    define MYPROJ_IF_64_BIT_ELSE( x64, x86 ) (x86)
    static_assert( sizeof( void* ) == 4, "Pointer size is unexpected for this bitness" );
#else
#    error "Unknown bitness!"
#endif

然后您可以像这样使用它:
#if MYPROJ_IS_BITNESS( 64 )
    DoMy64BitOperation()
#else
    DoMy32BitOperation()
#endif

或者使用我添加的额外宏:

MYPROJ_IF_64_BIT_ELSE( DoMy64BitOperation(), DoMy32BitOperation() );

4

“在64位编译”在C++中没有明确定义。

C ++仅为int,long和void *等大小设置下限。即使为64位平台编译,也不能保证int是64位的。该模型允许例如23位int和sizeof(int *)!= sizeof(char *)

64位平台有不同的编程模型

您最好进行特定于平台的测试。其次,可移植决策必须更具体地说明何为64位。


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