繁体   English   中英

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

[英]Determining 32 vs 64 bit in C++

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

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

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

谢谢。

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

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

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

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

  • 如果项目设置有缺陷或损坏(尤其是在 Visual Studio 2008 SP1 上),_WIN32 和 _WIN64 有时可能未定义。
  • 由于项目配置错误,标记为“Win32”的项目可能设置为 64 位。
  • 在 Visual Studio 2008 SP1 上,根据当前的 #define,有时智能感知不会使代码的正确部分变灰。 这使得在编译时很难准确地看到正在使用哪个 #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:”结尾,它会生成警告或错误。

之所以如此有效,是因为它迫使您提前考虑每一个案例,而不是依赖“else”部分中的(有时是有缺陷的)逻辑来执行正确的代码。

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

您应该能够使用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

一开始,这在 Windows 上是行不通的。 无论您是为 32 位还是 64 位窗口编译,Longs 和 ints 都是 32 位。 我认为检查指针的大小是否为 8 字节可能是更可靠的路由。

你可以这样做:

#if __WORDSIZE == 64
char *size = "64bits";
#else
char *size = "32bits";
#endif
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

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

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

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

您最好的选择是特定于平台的测试。 你的第二个最好的、便携的决定必须更具体地说明什么是 64 位。

您的方法并不太远,但您只是在检查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 位符号就会变得容易。 所以你会有这样的项目配置:

32位调试
32 位版本
64位调试
64 位版本

编辑:这些是通用配置,而不是目标配置。 随便给他们打电话。

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

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

借用上面Contango优秀答案并将其与 Fluent C++ 中的“ Better Macros, Better Flags ”相结合,您可以执行以下操作:

// 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() );

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

这是我一直在向最终用户传达程序是编译为 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

测试.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]

这里有更多的方法可以在现代 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>可能会令人困惑(另一个模板可能更好,逻辑上更正确),我仅将其用作满足编译器名称修改的解决方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM