您的位置:首页 > 其它

32位程序移植到64位需要考虑的问题

2014-02-23 23:29 766 查看

概述

从32位到64位,根本性的区别在于两种数据类型发生了变化:long和pointer。在32位环境下,两者长度都是32位,也就是4个字节;而在64位环境下都是8个字节。所以当你把pointer或者long型数据赋给int型时,会发生数据截断(data truncation)。

1、32位与64位数据模型之间的区别

32位应用的数据模型我们称作ILP32(之所以这样命名,是因为int,long,pointer都是32位),而64位应用则采用LP64模型(意味着long,pointer变成64位了,其他的不变)。在当前的32位环境下,我们可以认为int,long,pointer是等价的,因为它们占用相同的字节,于是就有很多混用的情况;但是到了64位的时代,long和Poniter的大小都改变了,原来的假设不再成立。

2、注意int和pointer转换

因为integer与pointer大小相同,所以32位代码中常常把pointer转换为int或者unsigned int,以便算术运算。为了移植,你可以把pointer转换为unsigned long,因为long和pointer都是等长的,无论是在ILP32或LP64。但是,为了使代码更清晰,推荐用uintptr_t,uintptr_t和intptr_t都需要包含头文件inttypes.h。

例如:下面代码在64位环境下编译出错:

char *p = &something;
p = (char *) ((int)p & PAGEOFFSET);

% cc ..
warning: conversion of pointer loses bits


改用uintptr_t后,无论是32位或者64位都没问题:

char *p = &something;
p = (char *) ((uintptr_t)p & PAGEOFFSET);

3、注意int和long转换

在ILP32中,可能从未对int和long加以区分,因此,混用的情况非常多,看下面代码:

int A = 0;
long B = 0;
long C = 0;
...
A = B + C;

% cc
warning: assignment of 64-bit integer to 32-bit integer

4、结构体字节对齐及大小问题

在结构体中,各个成员的起始地址都是对齐字节的倍数,例如:

struct T
{
int i;
long j;
int k;
char *p;
};
在ILP32中,sizeof(T)应该是16字节;在LP64中,应该是32!因为此时long/char *的对齐字节都变为8,为了保证满足对齐要求,i/k都被扩展为8字节了,又例如:

struct T
{
char *p;
long j;
int i;
int k;
};


此时,无需扩展,sizeof(T)=8+8+4+4=24.

5、注意union

例如:

union U
{
double _d;
long _l[2];
};
在ILP32中,两者大小相同都是8字节;移植到LP64,前者不变,后者为16字节,应改为:

union  U
{
double _d;
int     _l[2];
};


6、注意移位操作

无类型的整数常量就是 (unsigned) int 类型的,这可能会导致在位移时出现被截断的问题。

例如,在下面的代码中,a的最大值可以是 31。这是因为1 << a是 int 类型的:

long t = 1 << a;
要在 64 位系统上进行位移,应该使用1L,如下所示:

long t = 1L << a;


7、字符串格式化

函数printf 及其相关函数都可能成为问题的根源。例如,在 32 位系统上使用%d来打印 int 或 long 类型的值都可以,但是在 64 位平台上,这会导致将 long 类型的值截断成低 32 位的值。对于 long 类型的变量来说,正确的用法是%ld。

在下面的例子中,假设指针是 32 位的:

char *ptr = &something;
printf (%x\n", ptr);
上面的代码在 64 位系统上会失败,它只会显示低 4 字节的内容。解决方案是使用%p,%p兼容ILP32和L64,另外就是作为目标的buffer必须够长:

char *ptr = &something;
printf (%p\n", ptr);


8、类型定义

建议您不要使用 C/C++ 中那些在 64 位系统上会改变大小的数据类型来编写应用程序,而是使用一些类型定义或宏来显式地说明变量中所包含的数据的大小和类型,这些定义可以使代码的可移植性更好。例如,在sys/types.h中的数据类型,其大小会随ILP32或者LP64而变化:
* clock_t,    which represents the system time in clock ticks
* dev_t,      which is used for device numbers
* off_t,      which is used for file sizes and offsets
* ptrdiff_t,  which is the signed integral type for the result of subtracting two pointers
* size_t,     which reflects the size, in bytes, of objects in memory
* ssize_t,    which is used by functions that return a count of bytes or an error indication
* time_t,     which counts time in seconds
例如,sizeof和strlen返回值都是size_t类型,当它们的返回值赋值给int类型的变量时,在ILP32位上没有问题,但在LP64上并且返回值大于2G时会发生截断:
int bufferSize = (int) sizeof (something);
int length     = (int) strlen(str);


最好修改为:
size_t bufferSize = (size_t) sizeof (something);
size_t length     = (size_t) strlen(str);

9、注意边缘效应

局部代码发生类型改变,可能导致其他代码发生64位转换,例如函数的返回值由Int变为sszie_t,则所有调用该函数并获取其返回值的地方,都有可能发生意想不到的64位转换。

10、注意long array 对效率的影响

大型Long/unsigned long数组,在LP64下,相比ILP32,效率非常低,所以,如果int就足够,尽量不要使用Long,这一点,对于pointer arrary同样适用。

11、第三方类库

程序中可能实用了第三方类库,此时要检查第三方类库是否可以在64位机上使用。



提醒:应用程序在32位机移植到64位机时,由于long指针等大小的改变,会发生数据截断等错误,应该将编译器的警告级别调高,反复调试,有可能一个小小的警告会让程序死掉。另外在编译时采用-errchk=longptr64选项可以检查出把long/pointer表达式转换为int的情况,包括显式转换。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: