您的位置:首页 > 编程语言 > C语言/C++

32位C/C++程序移植到64位系统时需要注意的问题

2016-03-11 18:05 447 查看
概述

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

 

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

 

用lint检测long/pointer的64位问题

采用-errchk=longptr64选项可以检查出把long/pointer表达式转换为int的情况,包括显式转换。

 

留心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;

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

% cc ..

warning: conversion of pointer loses bits

 

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

char *p;

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

 

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

int waiting;

long w_io;

long w_swap;

...

waiting = w_io + w_swap;

% cc

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

 

留心对齐

出于访问的效率,结构中通常会有所谓的hole,用来保证其中的所有数据成员,起始地址都是对齐模数的倍数。

例如:

struct bar {

  int i;  

  long j; 

  int k; 

  char *p;

};

在ILP32中,sizeof(bar)应该是16字节;在LP64中,应该是32!因为此时long/char *的对齐模数都变为8,为了保证满足对齐要求,i/k都被扩展为8字节了。

又例如:

struct bar {

  char *p;

  long j;

  int i;

  int k;

}

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

 

留心union
union中的成员,必须保持平衡,也就是说,必须保证大小相等才有意义,所以移植时也要注意。

例如:

typedef union {

  double _d;

  long _l[2];

} llx_

在ILP32中,两者大小相同,都是8字节;移植到LP64,前者不变,后者为16字节,此时union已无意义,应改为:

typedef union {

  double _d;

  int _l[2];

} llx_

 

留心常量类型

在常量表达式中,精度的缺失会导致数据截断,例如:

int i = 32;

long j = 1 << i;

warning: left shift count >= width of type

什么意思?编译器抱怨左移的位数超过了数据类型的长度,结果就是j为0。

怎么修改呢?

int i = 32;

long j = 1L << i:

即可。

 

留心printf系列函数
在格式化串中,要注意:

%p兼容ILP32和L64。

如果有long型参数,必须加上l前缀;

另外就是作为目标的buffer必须够长。

 

留心sizeof

sizeof返回类型为unsigned long,如果返回给int型变量,可能发生截断。

 

留心derived data types

例如,这些定义在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

 

留心边缘效应

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

 

留心long array 对效率的影响

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

1.  对于long类型,会发生字节数变化,所以要检查所有使用long类型的地方;

2.  指针在32位系统中是4字节,而在64位系统中是8字节,因此所有跟指针字节数有关的地方必须改掉。典型的情况是:

      1)将struct类型直接存入文件再读出来,并且struct中包含指针成员。

      2)使用memset函数直接指定了大小,比如Student ** p = new Student*[num]; memset(p, 4, num);

针对反汇编代码整理的话,还需要考虑如下关键字:

operator new

memcpy

j__free

memset

void*
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: