在 C 中将 32 位应用程序转换为 64 位应用程序

Posted

技术标签:

【中文标题】在 C 中将 32 位应用程序转换为 64 位应用程序【英文标题】:Converting 32-bit Application Into 64-bit Application in C 【发布时间】:2010-09-29 12:49:38 【问题描述】:

我目前正在用 C 语言将 32 位应用程序转换为 64 位应用程序。该应用程序目前正在 x86 架构(Windows、osx、Unix、Linux)上工作。因此,在开始编码之前,我想知道在转换应用程序时需要考虑什么。

【问题讨论】:

我不肯定你的意思。你有你要移植的应用程序的来源吗?您是否建议将 ix86 二进制文件转换为 x86_64 二进制文件? 既然他指定了 C,我认为可以安全地假设他拥有应用程序的 C 代码。 :) 是的,我确实有应用的源码,但是我认为作者并不想把它改成64位。 【参考方案1】:
    找出是谁写的。他们是白痴吗?他们是几年前的吗?你能问他们问题吗?他们是否熟悉多个平台和系统的存在?了解程序作者的思维方式将帮助您在遇到问题时了解问题。 启动并运行 64 位机器/构建环境。 将 long 替换为 int。请充分注意LONG 不是long。 用intptr_t(unsigned int)&x 替换(int)&x 强制转换和键入uintptr_t 审核任何依赖于将结构转换为 char* 的内容以对其进行指针运算。 正则表达式搜索 \,以防您假设 4 = sizeof(void*) 请耐心等待。当您发现问题时,如果存在相同的问题,请在别处查找,并将解决方案封装在宏中。 尽量不要使用#ifdef RUN64 或类似的东西。如果 128 位平台流行起来,你会后悔的。 用一些集中的宏封装您的所有更改,这些宏将隐藏程序其他地方的可移植性差异。 使用覆盖率测试仪帮助确保您已覆盖所有内容(如果适用)

EDIT 按照评论的建议添加了 uintptr_t 注释。

【讨论】:

使用 uintptr_t 代替 intptr_t。您不需要在地址上签名。 大多数人不会对转换后的值进行操作;它们通常只是用来将一些值复制到回调中,所以 intptr_t 很好。虽然我同意差异是否对代码很重要,但这将是一个有趣的点。【参考方案2】:

一个尚未提及的潜在问题是,如果您的应用程序从磁盘读取或写入二进制数据(例如,使用 fread 读取结构数组),您将不得不非常仔细地检查,并且可能最终有两个阅读器:一种用于旧文件,另一种用于 64 位文件。或者,如果您小心使用 uint32_t 等来自 <stdint.h> 头文件的类型,您可以重新定义您的结构以逐位兼容。在任何情况下,二进制 I/O 都是需要注意的。

【讨论】:

【参考方案3】:

这实际上取决于应用程序及其编码方式。一些代码只需用 64 位编译器重新编译,就可以正常工作,但通常只有在设计代码时考虑到可移植性才会发生这种情况。

如果代码对本地类型和指针的大小有很多假设,如果它有很多位打包技巧,或者它使用字节指定协议与外部进程通信,但使用一些关于大小的假设那么原生类型可能需要一些或很多工作才能获得干净的编译。

几乎每个强制转换和编译器警告都是需要检查的危险信号。如果代码一开始没有“警告干净”,那么这也表明可能需要进行大量工作。

【讨论】:

【参考方案4】:

如果您为您的值使用了正确的类型 - 例如。 size_tptrdiff_tuintptr_tstdint.h 中的固定大小 int 类型(在适当的情况下) - 并且没有硬编码值大小,您的代码应该开箱即用。

【讨论】:

通常,uintptr_t 比 intptr_t 好——但除此之外……另外, 相对较新 (C99);我使用它们,但我有意识地决定这样做是可以的。也就是说,C99 可用的地方多于使用的地方。 'C99 可用的地方多于使用的地方。' - 现在标准已经 10 岁了,快到该死的时间了!但是 gcc 支持仍然不完整... ps: fixed intptr_t -> uintptr_t【参考方案5】:

C 中 32 位和 64 位编程之间的两个主要区别是 sizeof(void*)sizeof(long)。您将遇到的主要问题是大多数 Unix 系统使用定义 long 为 64 位的 I32LP64 标准,而 Win64 使用定义 long 为 32 位的 IL32LLP64 标准。如果您需要支持跨平台编译,您可能需要为 32 位和 64 位整数使用一组基于架构的 typedef,以确保所有代码的行为一致。这是作为 C99 标准的一部分的 stdint.h 的一部分提供的。如果您没有使用 C99 编译器,则可能需要滚动自己的等效项

如其他地方所述,转换的主要关注点是假定为sizeof(int) == sizeof(long) == sizeof(void*) 的代码、支持已写入磁盘的数据的代码和跨平台 IPC 的代码。

要详细了解这背后的历史,请查看来自 ACM Queue 的 article。

【讨论】:

【参考方案6】:

切换到 64 位时面临的主要问题是指针的大小不同(64 位而不是 32 位 - 呵呵)整数的大小和 long 的大小也可能不同,具体取决于平台。

为什么会出现这个问题?好吧,事实并非如此,除非您的代码假定 sizeof(int) == sizeof(void*)。这可能会导致令人讨厌的指针错误。

【讨论】:

【参考方案7】:

嗯,从根本上说,更改的数量相当少,但如果应用程序一开始没有经过仔细编写以使其具有一定的可移植性,这仍然是一项重大任务。

主要区别在于指针是 64 位宽的,但是大多数其他数据类型没有改变。 int 仍然是 32 位,而 long 可能也仍然是 32 位。因此,如果您的代码在整数和指针之间转换,那将会中断。同样,任何依赖于成员特定偏移量的结构或类似结构都可能会中断,因为其他成员现在可能更大,因此会更改偏移量。

当然,您的代码一开始就不应该依赖这些技巧,因此在理想情况下,这根本不是问题,您只需重新编译,一切都会正常工作。但你可能并不生活在一个理想的世界里...... ;)

【讨论】:

实际上,long 通常是问题所在,因为在 Windows 上 long 保持在 32 位,但在 Unix 上 long 从 32 位变为 64 位。【参考方案8】:

已经有很多好的答案了。

考虑使用Gimpel Lint。它可以准确指出有问题的构造类型。如果你的经验和我一样,它还会向你展示系统中的许多与 32/64 位端口无关的错误。

【讨论】:

@Darron:我发现 PC-lint(来自 Gimpel Software)发出的警告很神秘。我的建议是使用 Splint (splint.org)。 实际上我已经多年没有使用 Gimpel lint,因为我已经转向 Java 世界。如果我最终回到 C/C++ 领域,我会记住 Splint。

以上是关于在 C 中将 32 位应用程序转换为 64 位应用程序的主要内容,如果未能解决你的问题,请参考以下文章

在 C/C++ 中将 64 位 bmp 文件转换为数组

是否值得在 spark 数据帧中将 64 位整数转换为 32 位(16 位)整数?

如何将 32 位 *.lib 文件转换为 64 位 *.a 文件

将 32 位 android 应用程序转换为 64 位应用程序

C:在处理 32 位程序时,在 Windows 64 位机器上将 int 转换为 int* 时出现警告

将 32 位 dll 转换为 64 位