#ifdef 用于 32 位平台

Posted

技术标签:

【中文标题】#ifdef 用于 32 位平台【英文标题】:#ifdef for 32-bit platform 【发布时间】:2010-10-18 15:36:48 【问题描述】:

在我维护的一个应用程序中,我们遇到了影响标准库的文件描述符限制问题。此问题仅影响 32 位版本的标准库。

我已经为我的代码设计了一个修复程序并希望实现它,但仅限于为 32 位可执行文件编译时。 #ifdef 可以使用什么预处理器符号来确定代码是针对 32 位还是 64 位目标编译的?

编辑

不好意思,没提,代码是跨平台的,linux、windows、solaris等几个unix风格,主要是用GCC编译。我可以跨平台使用任何事实上的标准吗?

编辑 2

我发现了一些定义“__ILP23”和“__LP64”,它们似乎可以工作......here 的讨论解释了 unix 平台的背景。有人有使用这些定义的经验吗?这个可以用吗?

【问题讨论】:

这取决于平台。不同的操作系统使用不同的#defines。如果你幸运的话,Boost 有一个隐藏在某个地方的便携式包装器。但除此之外,您只需要检查特定于平台的那些。顺便问一下,您在哪个平台上运行? 编辑了问题...代码主要针对 windows、solaris 和 linux,部分还运行在 AIX 和 HP-UX 上。 只是一个想法:#if sizeof(int) == 64 @rstevens:我认为以下答案之一中的 cmets 表示 #if 和 sizeof(int) 是在不同时间执行的。当预处理器完成其工作时,sizeof 运算符尚未执行。 见***.com/questions/152016/… 【参考方案1】:

我会通过最大指针值常量间接测试它:

#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

这样您就不会依赖于任何特定于平台的架构定义,而是依赖于具有特定架构的直接后果 - 指针大小。

【讨论】:

如果您使用 C++11,那么您可以检查宏 SIZE_MAX 的值,这是 std::size_t 类型变量可以容纳的最大值。在 32 位平台上为 0xFFFFFFFF,在 64 位平台上为 0xFFFFFFFFFFFFFFFF。宏在标题&lt;cstdint&gt; 中提供。【参考方案2】:

我在 Windows 上使用这样的结构:

#如果定义(_WIN64) //64位代码 #elif 定义(_M_IX86) //32位代码 #别的 #error "未知平台" #万一

对比:

#如果定义(_WIN64) // 64位代码 #别的 // 32 位代码 #万一

在前一种解决方案中,由于#error,编译器将能够告诉您需要在哪里为新平台添加代码。如果您遇到既不是 64 位也不是 32 位的平台,这有助于可维护性。是的,_M_IX86 并不完全是 32 位的同义词,但我认为我们大多数人支持的唯一 32 位平台实际上是 x86。所以作为一个实际的措施就足够了。

在后面的解决方案中,您必须使用 grep 或类似的工具手动确定新平台需要代码的位置。这是乏味且容易出错的。

我想到下面的结构也是可以接受的,虽然我没有在生产中测试过,也没有真正考虑过。

#如果定义(_WIN64) //64位代码 #elif 定义(_WIN32) //32位代码 #别的 #error "未知平台" #万一

【讨论】:

【参考方案3】:

我建议将predef SourceForge 加入书签。没有唯一的答案,但它肯定可以帮助您入门。

编辑:对于仅限 GCC 的代码,您可以使用 __i386__ 来检查 32 位 x86 芯片,我建议尝试使用 __X86_64__ 或类似的东西来检查 64 位 x86 芯片。 (注意:我注意到之前涉及__ia86__的答案实际上是一个不同的芯片,而不是64位x86芯片。这只是表明我缺乏硬件经验。对于那些比我更了解硬件的人,领事我在上面链接到的关于预定义宏的 SourceForge 页面。它比我更准确。)还有一些其他的可以工作,但是这两个在 GCC 版本中应该是相当普遍的。

【讨论】:

ia64 是否适用于普通的 intel64 位编译?或者有没有amd64之类的? 我没有使用 64 位架构(或在非英特尔架构上编程)的经验,但根据 predefs 页面,有一个 64 位的 amd64 AMD。 ia64 似乎是特定于 x86 的。 i386 仅在为 intel cpu 编译时才有效,对吗?在 solaris 上,我们为 sparc-s2 编译。 作为另一个提示,你可以运行gcc -E -dM - &lt;/dev/null(或者离开&lt;/dev/null,在你按下回车键后输入EOF),GCC 会输出一个它包含的所有宏的列表-定义。在几个平台上执行此操作,并为每个平台编制一份独特且有用的列表。 @veefu - 是的,SPARC 芯片会有不同的宏。最有可能的是,您可以使用 sparc 或类似的东西。对于每个平台,您可以使用大约三个相似但不同的宏,尤其是对于 GCC。检查预定义页面。【参考方案4】:

至少 32 位 Solaris 有 256 个文件指针的限制,因为该结构将文件描述符存储在无符号字符字段中。保留这一点是为了向后兼容一些几乎不可能的旧版本 SunOS。其他平台——我想说大多数其他平台——没有这个限制。另一方面,普通用户程序需要同时打开这么多文件是比较少见的。它通常表示一个错误(完成后不关闭文件)而不是。话虽如此,但对于需要同时打开大量数据文件的数据库服务器来说,这可能是个问题。


一条评论说:

差不多了。我们没有打开大量文件,但服务器处理了大量来自客户端的连接。套接字句柄和文件描述符似乎来自同一个地方。当我们有很多连接时,'fopen' 会失败,因为系统级调用返回并且 fd > 255。

“套接字句柄”是系统调用级别的文件描述符,因此它们与文件的常规文件描述符来自同一位置。

如果你必须解决这个问题,那么你需要包装你当前的套接字打开代码,这样如果它得到一个 0..255 范围内的文件描述符,那么它会调用 'dup2()' 来创建一个文件描述符在 stdio 不会使用的范围内 - 然后关闭原始文件描述符。唯一的问题是您必须跟踪哪些文件描述符可用,因为dup2 将愉快地关闭目标文件描述符,如果它当前是打开的。

当然,我假设您的套接字代码读取文件描述符而不是文件指针。如果是这样的话,你的问题就更大了——太多的东西想要使用相同的资源,而它们不能同时使用它们。

【讨论】:

差不多了。我们没有打开大量文件,但服务器处理了大量来自客户端的连接。套接字句柄和文件描述符似乎来自同一个地方。当我们有很多连接时,'fopen' 会失败,因为系统级调用返回并且 fd > 255。 是的,这几乎就是我已经实现的。使用调用 'fcntl' 的代码包装对 'socket' 和 'accept' 的调用以在 【参考方案5】:

您可以检查一个众所周知的类型的大小,例如sizeof(int*) == 4 对于 32 位平台。

由于 sizeof 在编译时是已知的,我相信

if(sizeof(int*) == 4)

  ...

应该做的伎俩

编辑:cmets 是正确的,您需要使用常规 if,#if 将不起作用。

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

【讨论】:

这不适用于#ifdef。 sizeof 是编译时间,而 #if 是预处理器时间 同意。在 OS X Leopard 上的 GCC 4.0.1 上测试。 创建一个程序,作为构建过程的一部分运行测试,并将一些#defines输出到配置文件或Makefile中? 这将在同一个可执行文件/库中包含 32 位和 64 位代码。我不喜欢这种方法,因为它依赖于 4 或任何字节长的指针。不好的做法。 如果您只为 32 位平台构建,编译器只会实例化 32 位平台的代码。如果指针是 4 字节或 8 字节,则依赖关系的全部意义在于区分 32 位和 64 位平台【参考方案6】:

我不确定是否有合适的通用#if def。 C++ 标准几乎肯定没有定义一个。当然也有特定平台的。

例如,Windows

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

EDIT OP 提到这是使用 GCC 和其他编译器在 Windows 和非 Windows 之间进行的交叉编译

没有可用于所有平台和编译器的通用宏。一点预处理器的魔法虽然可以做到这一点。假设您只在 x86 和 amd64 芯片上工作,以下应该可以解决问题。不过,它可以很容易地扩展到其他平台

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

【讨论】:

好吧,听起来像这样,结合 Chris 的 cmets 进行的一些 gcc 调查可能会奏效。 +1 你们俩。【参考方案7】:

看看那个:

i386 macrosAMD64 macros

【讨论】:

【参考方案8】:

我最终可能会在 Makefile 中使用 uname 确定您是在 32 位平台还是 64 位平台上。然后,添加到您的 CFLAGS、-DX32 或 -DX64。你可以#ifdef X64。

但这只是一个统一的解决方案。我不确定我会在 Windows 上做什么。

【讨论】:

这样做的问题是,即使用于编译的系统是 64 位的,它也可能正在编译一个 32 位的可执行文件。不过,您是对的,这些信息必须以某种方式在 makefile 中公开。编译器必须被告知目标是 32 还是 64。应该能够适应。【参考方案9】:

C++ 标准没有定义这样的符号 - 您的特定平台(您的问题中没有指定)可能会提供一个。

【讨论】:

【参考方案10】:

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

【讨论】:

【参考方案11】:

我相信定义是_WIN64

【讨论】:

以上是关于#ifdef 用于 32 位平台的主要内容,如果未能解决你的问题,请参考以下文章

导出到多个平台时无法启动导出的64位产品

平台架构识别

#ifdef DEBUG 与 CMake 独立于平台

Paint.NET 5.0发布,不再支持32位 x86平台

32位平台代码向64位平台移植

如何在#ifdef 语句中使用/避免#pragma