编写 64 位程序时,源代码方面的实际区别是什么?例如,只是装配不同吗?这不像是 C++ 的 64 位版本。如果它像编译器上的一个选项一样简单,那么为什么更多的程序不是原生的 64 位?如果 32 位与 64 位 CPU 的唯一区别是寄存器大小,我看不出这会如何影响程序(除了能够寻址更多内存)。
5 回答
32 位/64 位移植的典型缺陷是:
程序员的隐含假设
sizeof(void*) == 4 * sizeof(char)
.
如果您做出这个假设并且例如以这种方式分配数组(“我需要 20 个指针,所以我分配 80 个字节”),您的代码会在 64 位上中断,因为它会导致缓冲区溢出。“小猫杀手”(re @SK-logic),
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 位代码之间的时序差异(由于不同的代码大小/缓存占用,或不同的内存访问特性/模式,或不同的调用约定)可能会破坏“校准”。说,
for (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
,根据大小整数从<stdint.h>
) 和 LP64 (UN*X 使用 -int
isint32_t
、long
isint64_t
和uintptr_t
/void*
isuint64_t
),但也有不同对齐规则的“细分” - 某些环境假设long
,float
或double
以各自的大小对齐,而其他人则假设它们以四个字节的倍数对齐。在 32 位 Linux 中,它们以 4 个字节对齐,而在 64 位 Linux 中,以 4 个字节和8 个字节的倍数float
对齐。 这些规则的结果是,在许多情况下,即使数据类型声明完全相同,结构/类成员的位和偏移量在 32 位和 64 位环境之间也是不同的。 除了影响数组/向量分配之外,这些问题还会影响数据输入/输出,例如通过文件 - 如果 32 位应用程序写入例如相同应用程序为 64 位读入重新编译的文件,结果将不会是所希望的。 刚刚给出的例子只是关于语言原语long
double
sizeof(struct { ...})
struct { char a; int b; char c, long d; double e }
(char
,int
,long
等) 但当然会影响各种平台相关/运行时库数据类型,无论是size_t
,off_t
,time_t
,HANDLE
, 基本上是任何非平凡的struct
// ...union
-class
所以这里的错误空间很大,
然后是较低级别的差异,例如手动优化装配(SSE/SSE2/...);32bit 和 64bit 有不同(数量)的寄存器,不同的参数传递规则;所有这些都会强烈影响此类优化的执行方式,并且很有可能例如在 32 位模式下提供最佳性能的 SSE2 代码将需要重写/需要增强以提供最佳性能 64 位模式。
还有 32 位和 64 位的代码设计约束非常不同,尤其是在内存分配/管理方面;一个经过仔细编码以“最大限度地利用它可以在 32 位中获得的内存”的应用程序将在如何/何时分配/释放内存、内存映射文件使用、内部缓存等方面具有复杂的逻辑 - 其中大部分将在 64 位中是有害的,您可以“简单地”利用巨大的可用地址空间。这样的应用程序可能会重新编译为 64 位就好了,但在那里的性能比一些没有最大化 32 位窥视孔优化的“古老的简单弃用版本”更差。
所以,归根结底,它也是关于增强/增益的,这就是更多工作的地方,部分是编程,部分是设计/需求。即使你的应用程序在 32 位和 64 位环境中都干净地重新编译并在两者上都经过验证,实际上是受益于 64 位?是否可以/应该对代码逻辑进行更改以使其在 64 位中做得更多/运行得更快?您可以在不破坏 32 位向后兼容性的情况下进行这些更改吗?对 32 位目标没有负面影响?增强功能将在哪里,您可以获得多少?
对于大型商业项目,这些问题的答案通常是路线图上的重要标志,因为您的起点是一些现有的“赚钱者”......
并非所有程序都必须重新编写才能在 64 位平台上运行。通常,重写程序是为了利用某些 64 位操作系统上更大的自由度,例如更大的地址空间,但这并不意味着它们必须重写才能工作。
有些程序可能必须重新编译,而源代码保持不变。一些使用中间表示和即时编译的编程环境允许相同的二进制文件在 32 位或 64 位操作系统上原样运行。
正如其他答案所述,在需要重写本机程序的地方,通常是由于对指针大小的假设。有些人可能会称这些假设为错误。
它们有不同的内存对齐问题,而且许多新手程序员都假设一切都是 32 位的(指针大小、文件偏移量、时间戳大小等)。
对于用编译语言正确编写的程序,需要做的就是重新编译它。唯一需要“重写”(甚至修改)的程序是那些一开始就写得不好的程序,它们会做出很多虚假的假设,并且可能会调用未定义的行为,比如狂野。
对于未编译为机器代码但以某种半解释/字节码/JIT 形式解释或运行的语言,实际上很难制作一个在从 32 位机器迁移到 64 位机器时中断的程序。它可能“破坏”的最有可能的方式就是使用更多内存并且可能会用完。
在 64 位系统中,指针通常是 64 位的。