146

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

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

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

谢谢。

4

16 回答 16

110

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

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

于 2009-10-01T18:26:32.537 回答
105
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;
}
于 2009-10-01T18:32:35.667 回答
47

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

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

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

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

于 2012-09-09T11:02:47.657 回答
36

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

于 2009-10-01T19:04:39.480 回答
15

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

于 2009-10-01T18:22:48.660 回答
10

你可以这样做:

#if __WORDSIZE == 64
char *size = "64bits";
#else
char *size = "32bits";
#endif
于 2012-12-27T07:05:32.907 回答
7
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
于 2012-01-23T12:34:44.523 回答
4

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

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

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

您最好的选择是针对特定平台的测试。您的次佳便携决定必须更具体地说明什么是 64 位。

于 2009-10-01T19:48:57.353 回答
4

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

  #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
于 2016-10-14T02:24:05.430 回答
3

您的方法并不太远,但您只是在检查大小是否long相同int。从理论上讲,它们都可能是 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 位。

于 2013-10-07T17:27:07.287 回答
3

人们已经提出了尝试确定程序是否正在编译的方法32-bit64-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
于 2015-06-01T16:25:52.260 回答
2

借鉴上面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() );
于 2020-04-06T15:54:15.970 回答
1

如果您可以在所有环境中使用项目配置,则可以轻松定义 64 位和 32 位符号。所以你会有这样的项目配置:

32 位调试
32 位发布
64 位调试
64 位发布

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

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

于 2009-10-01T18:38:12.503 回答
1

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

于 2009-10-22T11:42:19.170 回答
1

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

于 2021-08-14T23:07:29.467 回答
0

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

这是我一直在向最终用户传达程序是编译为 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]
于 2018-11-21T14:36:31.533 回答