#ifdef 用于32位平台

23
在我维护的应用程序中,我们遇到了影响stdlib的文件描述符限制问题。这个问题只会影响标准库的32位版本。
我已经为我的代码设计了一个修复方案,并希望在编译32位可执行文件时实现它。我可以使用什么预处理器符号来确定代码是为32位还是64位目标编译的?
编辑
对不起,没有提到,代码是跨平台的,Linux、Windows、Solaris和其他一些Unix平台,大多数使用GCC进行编译。有什么通用标准可以跨平台使用吗?
编辑2
我发现了一些定义"__ILP23"和"__LP64",看起来它们可能有效... 一个讨论解释了unix平台的背景。有人使用过这些定义吗?这将可行吗?

这是平台相关的。不同的操作系统使用不同的 #defines。如果你很幸运,Boost 可能在某个地方隐藏了一个可移植的包装器。但否则,你就只能检查特定于平台的那些了。顺便问一下,你正在运行哪个平台? - jalf
只是一个想法:#if sizeof(int) == 64 - mmmmmmmm
@rstevens:我认为下面其中一个答案中的注释说#if和sizeof(int)在不同的时间执行。当预处理器完成其工作时,sizeof运算符尚未被执行。 - veefu
可能是C++在Windows和Linux上编译:ifdef开关的重复问题。 - Cory Klein
可能是Determining 32 vs 64 bit in C++的重复问题。 - phuclv
显示剩余2条评论
11个回答

19

我不确定是否有适用的通用 #if def。C++标准几乎肯定没有定义这样一个东西。但确实存在某些特定于平台的定义。

例如,Windows

#if _WIN64 
// 64 bit build
#else
// 32 bit build
#endif

编辑:楼主提到这是使用GCC和其他编译器在Windows和非Windows之间进行交叉编译。

并不存在适用于所有平台和编译器的通用宏。然而,一些预处理器技巧可以解决问题。假设你只需要在x86和amd64芯片上工作,以下代码应该能奏效。但是它也可以很容易地扩展到其他平台。

#if _WIN64 || __amd64__
#define PORTABLE_64_BIT
#else
#define PORTABLE_32_BIT
#endif

好的,听起来这样做,再加上Chris的评论所提到的一些gcc研究,可能会奏效。你们两个都 +1。 - veefu

15

我建议收藏predef SourceForge。没有一个确定的答案,但这个网站肯定能帮助你入门。

编辑:对于仅限于GCC的代码,您可以使用__i386__来检查32位x86芯片,并建议尝试__X86_64__或类似内容来检查64位x86芯片。(注意:我注意到以前的回答涉及__ia86__实际上是不同的芯片,而不是64位x86芯片。这只是显示了我的硬件经验的不足。对于那些比我更了解硬件的人,请参考我上面链接的预定义宏的SourceForge页面。它比我更准确。)还有一些其他的也可以用,但这两个在GCC版本中应该是相当通用的。


在正常的intel64位编译中,__ia64__会为true吗?或者是否有类似于__amd64__的东西? - JaredPar
我没有使用过 64 位架构(或者非 Intel 架构上的编程)的经验,但是根据 predefs 页面,有一个针对 64 位 AMD 的 amd64__。__ia64 看起来是针对 x86 特定的。 - Chris Lutz
i386 只能在编译 Intel CPU 时使用,对吗?在 Solaris 上,我们编译 sparc-s2。 - veefu
3
另一个小建议是,您可以运行命令 gcc -E -dM - </dev/null (或者省略 </dev/null 并在按回车后键入 EOF),GCC 将输出其预定义的所有宏列表。在几个平台上执行此操作,并编译每个平台独特且有用的宏列表。 - Chris Lutz
@veefu - 是的,SPARC芯片将有一个不同的宏。最有可能的是,您可以使用__sparc__或类似的东西。对于每个平台,您可以使用大约三个类似但不同的宏,特别是对于GCC。请检查predefs页面。 - Chris Lutz
__ia64__在x86、x64或amd64构建中不会被定义 - ia64是一种完全不同的CPU架构,与x86没有任何关系(除了英特尔参与了它的开发)。 - Michael Burr

7

我会间接地测试它,使用最大指针值常量:

#include <stdint.h>

#if UINTPTR_MAX == 0xffFFffFF
// 32-bit platform
#elif UINTPTR_MAX == 0xffFFffFFffFFffFF
// 64-bit platform
#else
#error Unknown platform - does not look either like 32-bit or 64-bit
#endif

这样做的好处是不依赖于任何平台特定的架构定义,而是依赖于具有特定架构的直接结果 - 指针大小。

1
如果您使用C++11,则可以检查宏SIZE_MAX的值,该值是std::size_t类型变量可以容纳的最大值。在32位平台上,它为0xFFFFFFFF,在64位平台上为0xFFFFFFFFFFFFFFFF。该宏在头文件<cstdint>中提供。 - András Aszódi

6

5
你可以检查一个已知类型的大小,例如32位平台上sizeof(int*) == 4。
由于sizeof在编译时已知,我相信...
if(sizeof(int*) == 4)
{
  ...
}

应该可以解决问题。

编辑:评论是正确的,你需要使用常规的if语句,#if不起作用。

如果您正在使用C ++,您可以创建模板化代码,并让编译器根据sizeof()调用为您选择专业化。 如果您构建32位平台,则编译器仅实例化32位平台的代码。 如果您构建64位平台,则编译器仅实例化64位平台的代码。


1
这对于#ifdef是行不通的。sizeof是编译时,而#if是预处理器时间。 - JaredPar
同意。在 OS X Leopard 上测试了 GCC 4.0.1。 - Chris Lutz
创建一个程序,作为构建过程的一部分运行测试,并将一些#define输出到配置文件或Makefile中? - Thomas Padron-McCarthy
@Max Lybbert:我知道我来晚了,但是我刚在这个帖子上得到了一个赞,所以仍然有人在阅读这个。这是一种非关联论。在 x86-64 上,int 仍然是 32 位,如果你知道指令集如何工作,这是完全有意义的。我也因为以下原因给这个答案投了反对票,1)它不起作用,2)严格来说,在我们所谓的“32位平台”上,指针的宽度并不保证是32位。 - Bastien Léonard
我知道我在 Stack Overflow 上有一些评论是不准确的,现在我已经意识到了。感谢您找到这个问题,我已经将其删除了。 - Max Lybbert
显示剩余6条评论

2
我在Windows上使用以下这种结构:
#if defined(_WIN64)
   //64位代码
#elif  defined(_M_IX86)
   //32位代码
#else
#error "未知平台"
#endif
相比之下:
#if defined(_WIN64)
  // 64位代码
#else
  // 32位代码
#endif
在前一种解决方案中,由于#error指示,编译器将能够告诉您需要添加新平台的代码的位置。如果您遇到既不是64位也不是32位的平台,则可以提高可维护性。是的,_M_IX86并不完全等同于32位,但我认为我们大多数人支持的唯一32位平台实际上是x86。因此,作为一项实用措施,它足以满足要求。
在后一种解决方案中,您必须手动查找需要为新平台添加代码的位置,使用grep或类似工具。这很繁琐且容易出错。
我想以下这种结构也是可以接受的,尽管我没有在生产环境中测试过,也没有仔细考虑过它。
#if defined(_WIN64)
   //64位代码
#elif  defined(_WIN32)
   //32位代码
#else
#error "未知平台"
#endif

2
我可能会在Makefile中使用uname命令来确定您所在的平台是32位还是64位。然后,将-DX32或-DX64添加到您的CFLAGS中。这样,您就可以使用#ifdef X64来编写代码。
但这只是Unix环境下的解决方案。我不确定在Windows上该怎么做。

问题在于,即使用于编译的系统是64位的,它可能会编译出一个32位的可执行文件。不过你说得对,这些信息必须以某种方式在makefile中公开。编译器必须被告知目标是32位还是64位。应该能够适应这一点。 - veefu

2
至少32位的Solaris有256个文件指针的限制,因为该结构将文件描述符存储在无符号字符字段中。这是为了向后兼容一些几乎不可能的旧版本SunOS。其他平台(我倾向于说大多数其他平台)没有这种限制。另一方面,普通用户程序需要同时打开那么多文件相对较少,这更经常表明存在错误(未在完成后关闭文件)而不是没有。尽管如此,对于需要同时打开许多数据文件的数据库服务器等事物来说,这可能是一个问题。
一个评论说:
“差不多了。我们没有大量打开文件,但服务器处理了许多客户端连接。套接字句柄和文件描述符似乎来自同一位置。当我们有很多连接时,“fopen”失败,因为系统级调用返回并且fd > 255。”
“套接字句柄”是系统调用级别上的文件描述符,因此它们来自于与文件相关的常规文件描述符相同的位置。
如果您必须解决此问题,则需要包装当前套接字打开代码,以便如果它获得0..255范围内的文件描述符,则调用“dup2()”创建一个文件描述符,该文件描述符处于stdio不使用的范围内-然后关闭原始文件描述符。唯一的问题是,您必须跟踪可用的文件描述符,因为如果目标文件描述符当前打开,则“dup2”将快乐地关闭它。
当然,我假设您的套接字代码读取文件描述符而不是文件指针。如果情况是这样,那么您有更大的问题-太多东西想要使用相同的资源,它们不能同时使用它们。

那就差不多了。我们没有打开大量的文件,但服务器处理了大量来自客户端的连接。套接字句柄和文件描述符似乎来自同一位置。当我们有很多连接时,“fopen”会失败,因为系统级调用返回并且fd>255。 - veefu
是的,那几乎就是我实现的内容。我用代码包装了对“socket”和“accept”的调用,并使用调用“fcntl”来复制句柄,当<255时关闭原始句柄并使用更高的句柄。这很有效,但必须隔离到需要它的平台上。因此有关#ifdef的问题。 - veefu

1

这取决于您的操作系统和编译器,这些都是实现决策。


0

我认为定义是_WIN64


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