32位程序移植到64位需要考慮的問題

32位程序移植到64位需要考慮的問題
 
 
概述
從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同樣適用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章