32 位/64 位移植的典型缺陷是:
程序员的隐含假设 sizeof(void*) == 4 * sizeof(char)。如果您做出这个假设并且例如以这种方式分配数组(“我需要 20 个指针,所以我分配 80 个字节”),您的代码会在 64 位上中断,因为它会导致缓冲区溢出。
“小猫杀手”,int x = (int)&something; (反之亦然,void* ptr = (void*)some_int)。再次假设 sizeof(int) == sizeof(void*)。这不会导致溢出但会丢失数据 - 即指针的高 32 位。
这两个问题都属于称为类型别名的类(假设两种类型之间在二进制表示级别上具有同一性/可互换性/等效性),并且此类假设很常见;就像在 UN*X 上一样,假设 time_t、size_t、off_t 是 int,或者在 Windows 上,HANDLE、void* 和 long 是可互换的,等等......
关于数据结构/堆栈空间使用的假设(参见下面的 5.)。在 C/C++ 代码中,局部变量是在栈上分配的,32bit 和 64bit 模式使用的空间是不同的,这是由于以下几点,并且由于传递参数的规则不同(32bit x86 通常在栈上,64bit x86 部分在寄存器中)。在 32 位上使用默认堆栈大小的代码可能会导致 64 位上的堆栈溢出崩溃。这相对容易发现是导致崩溃的原因,但取决于应用程序的可配置性,可能难以修复。
32 位和 64 位代码之间的时序差异(由于不同的代码大小/缓存占用,或不同的内存访问特性/模式,或不同的调用约定)可能会破坏“校准”。比如说,对于 (int i = 0; i < 1000000; ++i) sleep(0); 32位和64位可能会有不同的时间......
最后是 ABI(应用程序二进制接口)。64 位和 32 位环境之间的差异通常比指针的大小更大……目前,存在 64 位环境的两个主要“分支”,IL32P64(Win64 使用的 - int 和 long 是 int32_t,只有 uintptr_t/void* 是 uint64_t,说就来自 ) 和 LP64 的大小整数而言(UN*X 使用的 - int 是 int32_t,long 是 int64_t 和 uintptr_t/void* 是 uint64_t),但也有不同对齐规则的“细分” - 有些环境假设 long , float 或 double 以它们各自的大小对齐,而其他人则假设它们以四个字节的倍数对齐。在 32 位 Linux 中,它们以 4 个字节对齐,而在 64 位 Linux 中,float 以 4 个字节对齐,long 和 double 以 8 个字节的倍数对齐。这些规则的结果是,在许多情况下,bith sizeof(struct { ...}) 和结构/类成员的偏移量在 32 位和 64 位环境之间是不同的,即使数据类型声明完全相同。除了影响数组/向量分配之外,这些问题还会影响数据输入/输出,例如通过文件 - 如果 32 位应用程序写入,例如 struct { char a; 诠释 b; 字符 c,长 d;double e } 到同一个应用程序重新编译为 64 位读入的文件中,结果将不是我们所希望的那样。刚刚给出的示例仅是关于语言原语(char、int、long 等),但当然会影响各种平台相关/运行时库数据类型,无论是 size_t、off_t、time_t、HANDLE,本质上是任何非平凡的结构/联合/类... - 所以这里的错误空间很大,}) 和结构/类成员的偏移量在 32 位和 64 位环境之间是不同的,即使数据类型声明完全相同。除了影响数组/向量分配之外,这些问题还会影响数据输入/输出,例如通过文件 - 如果 32 位应用程序写入,例如 struct { char a; 诠释 b; 字符 c,长 d;double e } 到同一个应用程序重新编译为 64 位读入的文件中,结果将不是我们所希望的那样。刚刚给出的示例仅是关于语言原语(char、int、long 等),但当然会影响各种平台相关/运行时库数据类型,无论是 size_t、off_t、time_t、HANDLE,本质上是任何非平凡的结构/联合/类... - 所以这里的错误空间很大,}) 和结构/类成员的偏移量在 32 位和 64 位环境之间是不同的,即使数据类型声明完全相同。除了影响数组/向量分配之外,这些问题还会影响数据输入/输出,例如通过文件 - 如果 32 位应用程序写入,例如 struct { char a; 诠释 b; 字符 c,长 d;double e } 到同一个应用程序重新编译为 64 位读入的文件中,结果将不是我们所希望的那样。刚刚给出的示例仅是关于语言原语(char、int、long 等),但当然会影响各种平台相关/运行时库数据类型,无论是 size_t、off_t、time_t、HANDLE,本质上是任何非平凡的结构/联合/类... - 所以这里的错误空间很大,除了影响数组/向量分配之外,这些问题还会影响数据输入/输出,例如通过文件 - 如果 32 位应用程序写入,例如 struct { char a; 诠释 b; 字符 c,长 d;double e } 到同一个应用程序重新编译为 64 位读入的文件中,结果将不是我们所希望的那样。刚刚给出的示例仅是关于语言原语(char、int、long 等),但当然会影响各种平台相关/运行时库数据类型,无论是 size_t、off_t、time_t、HANDLE,本质上是任何非平凡的结构/联合/类... - 所以这里的错误空间很大,除了影响数组/向量分配之外,这些问题还会影响数据输入/输出,例如通过文件 - 如果 32 位应用程序写入,例如 struct { char a; 诠释 b; 字符 c,长 d;double e } 到同一个应用程序重新编译为 64 位读入的文件中,结果将不是我们所希望的那样。刚刚给出的示例仅是关于语言原语(char、int、long 等),但当然会影响各种平台相关/运行时库数据类型,无论是 size_t、off_t、time_t、HANDLE,本质上是任何非平凡的结构/联合/类... - 所以这里的错误空间很大,
然后是较低级别的差异,例如手动优化装配(SSE/SSE2/...);32bit 和 64bit 有不同(数量)的寄存器,不同的参数传递规则;所有这些都会强烈影响此类优化的执行方式,并且很有可能例如在 32 位模式下提供最佳性能的 SSE2 代码将需要重写/需要增强以提供最佳性能 64 位模式。
还有 32 位和 64 位的代码设计约束非常不同,尤其是在内存分配/管理方面;一个经过仔细编码以“最大限度地利用它可以在 32 位中获得的内存”的应用程序将在如何/何时分配/释放内存、内存映射文件使用、内部缓存等方面具有复杂的逻辑 - 其中大部分将在 64 位中是有害的,您可以“简单地”利用巨大的可用地址空间。这样的应用程序可能会重新编译为 64 位就好了,但那里的性能比一些没有最大化 32 位窥视孔优化的“古老的简单弃用版本”更差。
所以,归根结底,它也是关于增强/增益的,这就是更多工作的地方,部分是编程,部分是设计/需求。即使你的应用程序在 32 位和 64 位环境中都干净地重新编译并在两者上都经过验证,实际上是受益于 64 位?是否可以/应该对代码逻辑进行更改以使其在 64 位中做得更多/运行得更快?您可以在不破坏 32 位向后兼容性的情况下进行这些更改吗?对 32 位目标没有负面影响?增强功能将在哪里,您可以获得多少?对于大型商业项目,这些问题的答案通常是路线图上的重要标志,因为您的起点是一些现有的“赚钱者”......