1.ILP32和LP64数据模型
32位环境涉及"ILP32"数据模型,是因为C数据类型为32位的int、long、指针。而64位环境使用不同的数据模型,此时的long和指针已为64位,故称作"LP64"数据模型。
Data type | Data length(32bit) | Data length(64bit) | Signed |
char | 8 | 8 | Y |
unsigned char | 8 | 8 | N |
short | 16 | 16 | Y |
unsigned short | 16 | 16 | N |
int | 32 | 32 | Y |
unsigned int | 32 | 32 | N |
long | 32 | 64 | Y |
unsigned long | 32 | 64 | N |
long long | 64 | 64 | Y |
point | 32 | 64 | N |
size_t | 32 | 64 | N |
ssize_t | 32 | 64 | Y |
off_t | 32 | 64 | Y |
2.向64位移植代码时的所有问题差不多都可以总结出一个简单的规律:千万不要认为int、long、指针的长度一样。
3.strlen返回size_t(它在LP64中是unsigned long),当赋值给一个int时,截断是必然发生的。而通常,截断只会在str的长度大于2GB时才会发生,这种情况在程序中一般不会出现。
4.那些以十六进制或二进制表示的常量,通常都是32位的。例如,无符号32位常量0xFFFFFFFF通常用来测试是否为-1:
#define INVALID_POINTER_VALUE 0xFFFFFFFF |
然而,在64位系统中,这个值不是-1,而是4294967295;在64位系统中,-1正确的值应为0xFFFFFFFFFFFFFFFF。要避免这个问题,在声明常量时,使用const,并且带上signed或unsigned。
const signed int INVALID_POINTER_VALUE = 0xFFFFFFFF; |
这行代码将会在32位和64位系统上都运行正常。
5.联合体问题(Union)
当联合本中混有不同长度的数据类型时,可能会导致问题。下面是一个常见的开源代码包,可在ILP32却不可在LP64环境下运行。代码假定长度为2的unsigned short数组,占用了与long同样的空间,可这在LP64平台上却不正确。
typedef struct {
unsigned short bom;
unsigned short cnt;
union {
unsigned long bytes;
unsigned short len[2];
} size;
} _ucheader_t;
6.符号扩展
要避免有符号数与无符号数的算术运算。在把int与long数值作对比时,此时产生的数据提升在LP64和ILP32中是有差异的。因为是符号位扩展,所以这个问题很难被发现,只有保证两端的操作数均为signed或均为unsigned,才能从根本上防止此问题的发生。
long k;
int i = -2;
unsigned int j = 1;
k = i + j;
printf("Answer: %ld\n", k);
你无法期望答案是-1,然而,当你在LP64环境中编译此程序时,答案会是4294967295。原因在于表达式(i+j)是一个 unsigned int表达式,但把它赋值给k时,符号位没有被扩展。要解决这个问题,两端的操作数只要均为signed或均为unsigned就可。
7.字节序问题(Endian)
因64位平台的差异,在移植32位程序时,可能会失败,原因可归咎于机器上字节序的不同。Intel、IBM PC等CISC芯片使用的是Little-endian,而Apple之类的RISC芯片使用的是Big-endian;小尾字节序(Little- endian)通常会隐藏移植过程中的截断bug。
long k;
int *ptr;
int main(void)
{
k = 2 ;
ptr = &k;
printf("k has the value %ld, value pointed to by ptr is %ld\n", k, *ptr);
return 0;
}
这是一个有此问题的明显例子,一个声明指向int的指针,却不经意间指向了long。在ILP32上,这段代码打印出2,因为int与long长度一样。但到了LP64上,因为int与long的长度不一,而导致指针被截断。不管怎么说,在小尾字节序的系统中,代码依旧会给出k的正确答案2,但在大尾字节序(Big-endian)系统中,k的值却是0。
8.移植到64位平台之后的性能降低
当代码移植到64位平台之后,也许发现性能实际上降低了。原因与在LP64中的指针长度和数据大小有关,并由此引发的缓存命中率降低、数据结构膨胀、数据对齐等问题。
9.64位体系结构出现的原因在于应用需要更大的寻址空间。这些应用可能是高性能的服务器,数据管理系统,CAD或者游戏。这些应用将从64位地址空间和更多地寄存器中得到大量的性能提升。需要大量的内存的应用可以期待着有更高的性能提升。
10.打开和关闭编译时警告:
-w的意思是关闭编译时的警告,也就是编译后不显示任何warning,因为有时在编译之后编译器会显示一些例如数据转换之类的警告,这些警告是我们平时可以忽略的。
-Wall选项意思是编译后显示所有警告。
-W选项类似-Wall,会显示警告,但是只显示编译器认为会出现错误的警告。
在编译一些项目的时候可以-W和-Wall选项一起使用。