[英]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;
}
不幸的是,在跨平台、交叉编译器环境中,没有单一可靠的方法可以完全在编译时执行此操作。
因此,唯一可靠的方法是结合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
在 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
一般规则是“每个#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
来自@AI.G
评论:
4 年后(不知道之前是否可行)您可以使用静态断言将运行时检查转换为编译时检查:static_assert(sizeof(void*) == 4);。 现在一切都在编译时完成了:)
顺便说一句,可以调整上述规则以使您的整个代码库更可靠:
之所以如此有效,是因为它迫使您提前考虑每一个案例,而不是依赖“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 位int
和sizeof(int *) != sizeof(char *)
64 位平台有不同的编程模型。
您最好的选择是特定于平台的测试。 你的第二个最好的、便携的决定必须更具体地说明什么是 64 位。
您的方法并不太远,但您只是在检查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 位。
人们已经建议了一些方法来尝试确定程序是以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.