從32位到64位,根本性的區別在於兩種數據類型發生了變化:long和pointer。在32位環境下,顧名思義,兩者長度都是32位,也就是4個字節;而在64位環境下,都是8個字節。所以,當你把pointer或者long型數據賦給int型時,會發生數據截斷(data truncation)。
32位應用的數據模型我們稱作ILP32(之所以這樣命名,是因爲int,long,pointer都是32位),而64位應用則採用LP64模型(意味着long,pointer變成64位了,其他的不變),在當前的32位環境下,我們可以認爲int,long,pointer是等價的,因爲它們佔用相同的字節嘛,於是,就有很多混用的情況;但是,到了64位的時代,long和Poniter的大小都改變了,原來的假設不再成立。
採用-errchk=longptr64選項可以檢查出把long/pointer表達式轉換爲int的情況,包括顯式轉換。
因爲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
char *p;
p = (char *) ((uintptr_t)p & PAGEOFFSET);
在ILP32中,可能從未對int和long加以區分,因此,混用的情況非常多,看下面代碼:
int waiting;
long w_io;
long w_swap;
...
waiting = w_io + w_swap;
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中的成員,必須保持平衡,也就是說,必須保證大小相等纔有意義,所以移植時也要注意。
例如:
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:
即可。
在格式化串中,要注意:
%p兼容ILP32和L64。
如果有long型參數,必須加上l前綴;
另外就是作爲目標的buffer必須夠長。
sizeof返回類型爲unsigned long,如果返回給int型變量,可能發生截斷。
例如,這些定義在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/unsigned long數組,在LP64下,相比ILP32,效率非常低,所以,如果int就足夠,儘量不要使用Long,這一點,對於pointer arrary同樣適用。