确定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个回答

4
你的思路不太偏离,但你只是检查 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 位。

需要注意的是,在定义 UINT_MAX 和 ULONG_MAX 之前,您可能希望在 #if 测试之前的某个地方加入 #include <limits.h> - Alexis Wilke

3

已经有人提出了一些方法来确定程序是在32位还是64位编译的。

我想补充一下,你可以使用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
以下是一些在现代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>可能会让人感到困惑(另一个模板可能更好,更符合逻辑),我只是用它作为解决编译器名称重整的一种方法。


1

如果您可以在所有环境中使用项目配置,那将使定义64位和32位符号变得容易。因此,您可以拥有以下项目配置:

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

编辑:这些是通用配置,不是针对性的配置。可以随意更改名称。

如果您无法这样做,我喜欢Jared的想法。


或者将两种方法结合起来:对于你已知的编译器自动检测配置,但在遇到无法识别的编译器时,则回退到查看项目/命令行/其他地方指定的 #define。 - Steve Jessop
5
你的VisualStudio专用解决方案会如何帮助OP的跨平台问题? - alex tingle
我说过,只要支持项目配置。 - Jon Seigel
3
@Jon:嗯,根据定义,它们在任何跨平台环境中都不受支持。除非这是微软对跨平台的定义 - 能够在较新版本的Windows上使用。 - EFraim
1
@EFraim:是的,你可以使用VS来定位32位或64位,但这不是我所说的。通用项目配置和我指定的名称与平台完全无关。如果项目配置是特定于VS的,那就太糟糕了,因为它们非常方便。 - Jon Seigel
1
我认为这是正确的答案。与尝试自动检测事物相比,它更可靠。我见过的所有IDE都以某种形式支持此功能,我敢打赌我从未见过的也支持它。如果您使用make或jam,可以在调用时以通常的方式从命令行设置变量。 - please delete me

1

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


2
这将类似于构建系统为您提供标志,例如-DBUILD_64BIT。通常,某些东西在32位和64位上非常相似,因此将其放在同一文件中可以非常实用。 - Alexis Wilke
维护双源文件容易出错。在我看来,即使是一个巨大的#if bit64..所有代码,对于64位#else..所有代码,对于32位#endif也比那更好。(逐行处理#if是理想的) - brewmanz

0
我正在添加此答案作为运行时检查的用例和完整示例,详见另一个答案
这是我一直在采用的方法,用于向最终用户传达程序是作为64位还是32位(或其他)编译的:

version.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]

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