在 C + + 中确定32比64位

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

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


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

谢谢。

145388 次浏览

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

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

首先,我选择我自己的代理人。我更喜欢环境64/环境32。然后,我找出所有主要的编译器使用什么来确定它是否是一个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

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

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;
}

如果可以在所有环境中使用项目配置,那么定义一个64位和32位符号将变得很容易。所以你会有这样的项目配置:

32位调试器
32位发布
64位调试器
64位版本 < br/>

编辑: 这些是通用配置,不是目标配置。你想怎么称呼它们都行。

如果你做不到,我喜欢 Jared 的主意。

您应该能够使用在 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 》杂志网站(http://msinttypees.googlecode.com/svn/tree/stdint.h)

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

C + + 只对 int、 long 和 void *这样的大小设置较低的限制。即使在为64位平台编译 int 时,也不能保证它是64位的。该模型允许23位 intsizeof(int *) != sizeof(char *)

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

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

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

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

不幸的是,在一个跨平台、跨编译器的环境中,没有一个单一可靠的方法可以在编译时完全执行此操作。

  • 如果项目设置有缺陷或损坏(特别是在 VisualStudio2008SP1上) ,_ WIN32和 _ WIN64有时都可能没有定义 都有
  • 由于项目配置错误,标记为“ Win32”的项目可以设置为64位。
  • 在 VisualStudio2008SP1上,有时智能感知不会根据当前的 # Definition 使代码的正确部分变成灰色。这使得在编译时很难确切地看到正在使用哪个 # Definition。

因此,只有可靠方法是结合 3个简单的检查:

  • 1) 编译时间设置及;
  • 2) 运行时间检查及;
  • 3) 健壮的编译时间检查.

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

选择 准备好了所需要的 # Definition 变量的任何方法。我建议使用@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: 健壮的编译时检查

一般的规则是“每个 # Definition 必须以 # 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 _ asser(sizeof (void *) = = 4) ; . 现在,一切都在编译时完成了 :)

附录 A

顺便说一句,上面的规则可以修改,使您的整个代码库更加可靠:

  • 每个 if ()语句都以“ else”结尾,这将生成一个警告或错误。
  • 每个 switch ()语句都以“ default:”结束,这将生成一个警告或错误。

这种方法之所以能够很好地工作,是因为它迫使您提前考虑每一种情况,而不是依赖“ else”部分中的逻辑(有时是有缺陷的)来执行正确的代码。

我使用这种技术(在许多其他技术中)编写了一个30,000行的项目,从它第一次被部署到生产环境(那是12个月前)开始,它就完美地工作了。

你可以这样做:

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

您的方法并不太远,但您只是检查 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

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

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

人们已经提出了一些方法,这些方法将尝试确定程序是在 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

下面的代码适用于大多数当前环境:

  #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

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

这是我一直采用的方法,用来向终端用户传达程序是编译为64位还是32位(或者其他类似的编译方式) :

版本 h

#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

Test.cc

#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]

借用 康坦戈上面的回答很好并结合 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() );

以下是一些在现代 C + + 中实现所需功能的方法。

您可以创建一个定义系统位数的变量:

static constexpr size_t sysbits = (CHAR_BIT * sizeof(void*));

然后在 C + + 17中你可以这样做:

void DoMy64BitOperation() {
std::cout << "64-bit!\n";
}


void DoMy32BitOperation() {
std::cout << "32-bit!\n";
}


inline void DoMySysBitOperation()
{
if constexpr(sysbits == 32)
DoMy32BitOperation();
else if constexpr(sysbits == 64)
DoMy64BitOperation();
/*else - other systems. */
}

或者在 C + + 20中:

template<void* = nullptr>
// template<int = 32>  // May be clearer, pick whatever you like.
void DoMySysBitOperation()
requires(sysbits == 32)
{
std::cout << "32-bit!\n";
}


template<void* = nullptr>
// template<int = 64>
void DoMySysBitOperation()
requires(sysbits == 64)
{
std::cout << "64-bit!\n";
}


template<void* = nullptr>
void DoMySysBitOperation()
/* requires(sysbits == OtherSystem) */
{
std::cout << "Unknown System!\n";
}

通常不需要 template<...>,但是由于这些函数具有相同的错综复杂的名称,我们必须强制编译器选择正确的函数。此外,template<void* = nullptr>可能会令人困惑(另一个模板可能更好,逻辑上更正确) ,我只是使用它作为一个解决方案,以满足编译器名称错误。