在C ++中确定32位和64位


136

我正在寻找一种可靠地确定C ++代码是否以32位和64位编译的方法。我们已经提出了我们认为使用宏的合理解决方案,但是很想知道人们是否可以想到这种情况可能会失败,或者是否有更好的方法来做到这一点。请注意,我们正在尝试在跨平台的多编译器环境中执行此操作。

#if ((ULONG_MAX) == (UINT_MAX))
# define IS32BIT
#else
# define IS64BIT
#endif

#ifdef IS64BIT
DoMy64BitOperation()
#else
DoMy32BitOperation()
#endif

谢谢。


8
如果您真的在乎架构的字长,那么不要忽略它既不是32位也不是64位的可能性。您知道那里有16位和128位架构。
亚历克斯·廷格

64位和32位操作之间有什么区别?
peterchen

2
您真的不应该以目标平台的字宽为条件。而是直接使用相关数据类型的大小来确定要执行的操作。stdint.h可能是您的朋友,或者您可能需要开发自己的一些适当的typedef。
菲尔·米勒

此测试似乎不适用于Visual Studio 2008 SP1。对于32位和64位,它都卡在“ IS64BIT”上。
Contango 2012年

Answers:


99

不幸的是,没有跨平台宏可以在主要编译器中定义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

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


3
好吧,除了GCC和VS,还有其他编译器。例如,我想到了QNX和GHS(尽管我怀疑QNX具有与GCC类似的构建时间定义)。另外,您在GCC检查中忘记了MIPS64和IA64体系结构
Rom 2009年

14
@Rom,肯定有两个以上的编译器和体系结构。这仅是作为解决此问题的方法的一个示例,而不是完整的解决方案。
JaredPar

2
我说“通常”。“理想地”可能更现实。
史蒂夫·杰索普

7
我认为您应该使用“ #if defined(WIN32)|| defined(_WIN64)”等
KindDragon 2013年

3
#if _WIN32 || _WIN64... #elif __GNUC__... #else # error "Missing feature-test macro for 32/64-bit on this compiler."
戴维斯洛

100
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,会发生什么情况?
杰斯珀,

16
@Jesper,然后您将在上面的示例中获得链接错误。或者,您可以针对这种情况实施DoMyOperation
Kirill V. Lyadvinsky

1
巧妙地使用模板和荣誉来测试重要内容(某些特定类型的大小)而不是相关性。
菲尔·米勒

2
小心使用size_t。例如,您可能会遇到与指针大小不对应的问题(例如,在具有多个指针大小的平台上)。
Logan Capaldo

8
Standard表示的大小size_t足以容纳系统中任何已分配对象的大小。通常,这是您在条件编译时要了解的内容。如果不是您想要的内容,则可以将此代码段与其他类型一起使用,而不是size_t。例如,可能是void*
2009年

44

不幸的是,在跨平台,跨编译器的环境中,没有一种可靠的方法可以在编译时完全做到这一点。

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

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

  • 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

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

附录A

偶然地,上述规则可以进行调整以使您的整个代码库更可靠:

  • 每个if()语句均以“ else”结尾,该警告会生成警告或错误。
  • 每个switch()语句均以“ default:”结尾,该警告会生成警告或错误。

之所以如此有效,是因为它迫使您提前考虑每种情况,而不是依靠“其他”部分中的(有时是有缺陷的)逻辑来执行正确的代码。

我使用了这项技术(以及其他许多技术)来编写一个30,000行的项目,该项目从首次部署到生产的那天(即12个月前)就一直完美无缺。


sizeof(void*)是在编译时还是运行时解决?如果是在编译时,则在运行时检查始终为if(8!=8){...}
Ameen

@ameen在运行时已解决。该检查的目的是确保如果位数不是预期的那样,程序会以适当的错误退出。这意味着开发人员可以立即修复此错误,而不是尝试诊断随后会弹出的细微错误。
Contango

3
4年后(不知道之前是否可能),您可以使用静态assert:将运行时检查转换为编译时检查static_assert(sizeof(void*) == 4);。现在,所有这些都在编译时完成了:)
Al.G.

1
static_assert(sizeof(void*) * CHAR_BIT == 32)更具表现力和技术上的正确性(尽管我不知道字节数与8的字节数不同的任何体系结构)
Xeverous

1
另请参阅以下我的答案,该答案与Fluent C ++的“ 更好的宏,更好的标记 ” 相结合。
金属

30

您应该能够使用中定义的宏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

Microsoft的某些(全部?)编译器版本不附带stdint.h。不知道为什么,因为它是标准文件。这是您可以使用的版本:http://msinttypes.googlecode.com/svn/trunk/stdint.h


4
为什么没有Microsoft的stdint.h?因为它是随C99标准引入的,所以Microsoft似乎对实现C99中最简单的东西抱有积极的反感。即使是不需要编译器更改的简单库文件。即使是为C ++编译时已经完成的工作(例如语句后的声明)。我知道它需要测试等,但是我也知道MS从Dinkumware / Plauger获得(或曾经获得)相当一部分的库,并且Dinkumware拥有C99库的东西已经有很多年了。
Michael Burr

2
VC ++ 2010(始终为Beta 1)具有<stdint.h><cstdint>。至于目前的情况-VC ++库源自Dinkumware(仍然如此-TR1也来自那里),但是根据我在VCBlog上阅读的内容,它经过了相当重要的重构,可以干净地编译/clr,与所有MSVC一起使用。非标准类型,例如__int64,等等-这就是为什么它不像将其放入下一个编译器版本那样简单。
帕维尔米纳夫

2
这使我得到了正确的答案,但是我认为您应该将其与UINT64_MAX而不是INT64_MAX进行比较。我使用SIZE_MAX == UINT64_MAX-可能一样
Arno

15

首先,这在Windows上将不起作用。无论您是针对32位还是64位窗口进行编译,long和int均为32位。我认为检查指针的大小是否为8个字节可能是更可靠的路由。


2
不幸的是,#if指令中禁止sizeof(如果您考虑过,预处理器无法告知)
EFraim

是的,这就是为什么我只建议检查指针的大小而不是使用sizeof的原因-我想不出一种便携式的方法来使它脱颖而出...
mattnewport,2009年

3
问题没有(还)说,它已经在预处理器的时间内完成。许多/大多数启用了优化功能的编译器都可以很好地消除死代码,即使您使用像这样的测试“将其保留到运行时”也是如此sizeof(void*) == 8 ? Do64Bit() : Do32Bit();。那仍然可以在二进制文件中保留一个未使用的函数,但是该表达式很可能只是为了调用“ right”函数而编译的。
史蒂夫·杰索普

1
@onebyone解决了函数调用的问题,但是如果我想基于平台声明变量为其他类型,那将需要在预处理器中完成,除非您要声明多个变量并基于if语句使用它们(如果不使用它们,也会对其进行优化,但是在代码中不会太令人愉悦)
Falaina

1
那就对了,条件表达式中的常量表达式是不好的。不过,基里尔的方法可以做您想要的事情: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;
史蒂夫·杰索普

9

您可以这样做:

#if __WORDSIZE == 64
char *size = "64bits";
#else
char *size = "32bits";
#endif

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

6
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 2012年

4

C ++中没有很好地定义“以64位编译”。

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

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

最好的选择是针对特定平台的测试。你的第二个最好的,便携的决定必须在更具体的是什么是64位。


3

您的方法相差不远,但是您仅在检查longint大小是否相同。从理论上讲,它们都可以是64位,在这种情况下,假设两者均为32位,则检查将失败。这是一个检查,实际上检查类型本身的大小,而不是它们的相对大小:

#if ((UINT_MAX) == 0xffffffffu)
    #define INT_IS32BIT
#else
    #define INT_IS64BIT
#endif
#if ((ULONG_MAX) == 0xfffffffful)
    #define LONG_IS32BIT
#else
    #define LONG_IS64BIT
#endif

原则上,您可以对具有系统定义的最大值的宏的任何类型执行此操作。

请注意,long long即使在32位系统上,该标准也要求至少为64位。


需要注意的一件事是,对于要定义的UINT_MAX和ULONG_MAX,您可能希望#include <limits.h>#if测试之前有某个地方。
亚历克西斯·威尔克

3

人们已经建议了一些方法,这些方法将尝试确定程序是否在32-bit或中编译64-bit

我想补充一点,您可以使用c ++ 11功能static_assert来确保体系结构符合您的想法(“放松”)。

因此,在定义宏的地方:

#if ...
# define IS32BIT
  static_assert(sizeof(void *) == 4, "Error: The Arch is not what I think it is")
#elif ...
# define IS64BIT
  static_assert(sizeof(void *) == 8, "Error: The Arch is not what I think it is")
#else
# error "Cannot determine the Arch"
#endif

static_assert(sizeof(void*) * CHAR_BIT == 32)更具表现力和技术上的正确性(尽管我不知道字节数与8的字节数不同的任何体系结构)
Xeverous

2

以下代码可在大多数当前环境中正常运行:

  #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

3
请注意,_WIN64要求您已经包括了<windows.h>。使用Visual C ++,最好使用内置的编译器定义:_M_IX86_M_X64_M_ARM_M_ARM64,等
查Walbourn

对于PowerPC,我相信你需要检查__ppc64____powerpc64___ARCH_PPC64。这也适用于AIX和其他平台。
jww

1

如果您可以在所有环境中使用项目配置,则可以轻松定义64位和32位符号。因此,您将具有以下项目配置:

32位调试
32位发行版
64位调试
64位发行版

编辑:这些是常规配置,不是目标配置。随便叫他们。

如果您做不到,我会喜欢Jared的想法。


或两者结合:自动检测您所知道的编译器上的配置,但是可以回过头来查看在项目/命令行/任何在无法识别的编译器上指定的#define。
史蒂夫·杰索普

4
您的VisualStudio专用解决方案如何解决OP的跨平台问题?
亚历克斯·廷格

3
@乔恩:嗯。根据定义,它们在任何类型的跨平台环境中均不受支持。除非它是MS的跨平台定义,否则它适用于更新版本的Windows。
EFraim

1
@EFraim:是的,您可以使用VS定位32位或64位,但这不是我要说的。通用项目配置以及我为其分配的名称与平台绝对无关。如果项目配置是特定于VS的,那么这很遗憾,因为它们非常方便。
乔恩·塞格尔

1
我认为这是正确的答案。比尝试自动检测事物更可靠。我见过的所有IDE都以某种形式支持此功能,我敢打赌我从未见过的IDE也都支持它。如果使用make或jam,则可以按通常的方式在调用时从命令行设置变量。

1

我将32位和64位源放置在不同的文件中,然后使用构建系统选择适当的源文件。


2
这类似于让构建系统为您提供标志,例如-DBUILD_64BIT。通常,某些事物与32位和64位都非常相似,因此将其保存在同一文件中可能非常实用。
亚历克西斯·威尔克2014年

维护双源文件容易出错。IMO甚至对于32位#endif来说,对于64位#else ..所有代码来说,巨大的#if bit64 ..所有代码都比这更好。(我认为
#if

1

借鉴以上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://stackoverflow.com/a/12338526/201787
#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() );

0

我将这个答案作为用例和另一个答案中描述的运行时检查的完整示例添加。

无论程序是编译为64位还是32位(就此而言,还是其他),这都是我一直在传达给最终用户的方法:

版本

#ifndef MY_VERSION
#define MY_VERSION

#include <string>

const std::string version = "0.09";
const std::string arch = (std::to_string(sizeof(void*) * 8) + "-bit");

#endif

测试文件

#include <iostream>
#include "version.h"

int main()
{
    std::cerr << "My App v" << version << " [" << arch << "]" << std::endl;
}

编译测试

g++ -g test.cc
./a.out
My App v0.09 [64-bit]
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.