32 位和 64 位C数据类型
时间:2010-08-12 来源:海上的帆
char 通常被定义成 8 位宽。
int 通常被定义成 16 位或 32 位宽(或更高),它取决于平台。编译器将在这两者间选择最合适的字宽。
short 通常被定义成 16 位宽。
long 通常被定义成 32 位宽。
C99 为 C 语言扩展了新的整数类型 long long ,通常被定义成 64 位宽。(GNU C 亦支持)
但是 C 标准并没有定义具体的整数类型的宽度,只定义了 long long 的级别高于 long ,long 的级别高于 int ,int 的级别高于 short ,short 的级别高于 char 。(另外有 _Bool 永远是最低级别)。级别高的整数类型的宽度大于等于级别较低的整数类型。
char 的宽度用宏 CHAR_BIT 定义出来,通常为 8 ,而除了位域类型外,其它所有类型的位宽都必须是它的整数倍。
如果需要精确确定整数类型的宽度,在 C99 以及 GNU C 中,需要包含 stdint.h ,使用其中几种扩展的整数类型。
int16_t 可以保证整数长度为精确的 16 位。同样,int8_t 可以保证整数长度为精确的 8 位。类似的还有 int32_t int64_t 以及无符号类型 uint8_t 等等。
int_least16_t 可以得到一个当前平台所支持的至少有 16 位宽的最短整数类型。(其它同类型的可以类推)
int_fast32_t 可以得到当前平台下得到处理速度最快的至少为 32 位的整数类型。
intmax_t 可以获得当前平台所支持的最大宽度的整数类型。
在书写整数常数时,在数字后加上 L 表示是一个 long 类型的整数,加上 U 表示是一个 unsigned 整数,加上 LL 表示是一个 long long 类型整数。但是由于 long long 这些并不能确定准确字宽,有此需求时,可以借助宏 INTn_C(value) 来转换。例如,想指定一个 64 位整数常数 0x1234 ,可以写成 INT64_C(0x1234) 。若在当前平台上 int_least64_t 就是 long long int 的话,这个宏会被展开成 0x1234LL 。
最后再提一个很重要的扩展类型:intptr_t (无符号版本写成 uintptr_t)这个类型可以被安全的在 void * 和 整数间转换,对于写跨 64 位平台的程序非常重要。也就是说,当你需要把指针作为一个整数来运算时,转换成 intptr_t 才是安全的,可以在运算完毕安全的转回指针类型。
C 语言中,指针和整数之间的转换经常用到(多用于需要精确控制数据在内存中的精确布局时),在 32 位平台上,由于指针类型的字宽和 int 相同,所以我们不太在意这个问题。但是到了 64 位平台上,由于目前几乎所有 64 位系统都采用 LP64 模型,既整数依旧是 32 位,而指针是 64 位的。intptr_t 这个数据类型就成了安全跨平台编程的保证。
64 位的优点:64 位的应用程序可以直接访问 4EB 的内存和文件大小最大达到4 EB(2 的 63 次幂);可以访问大型数据库。本文介绍的是64位下C语言开发程序注意事项。
1 32 位和 64 位C数据类型
32和64位C语言内置数据类型,如下表所示。
上表中第一行的大写字母和数字含义如下所示:
I表示:int类型
L表示:long类型
P表示:pointer指针类型
32表示:32位系统
64表示64位系统
如:LP64表示,在64位系统下的long类型和pointer类型长度为64位。
64位Linux 使用了 LP64 标准,即:long类型和pointer类型长度为64位,其他类型的长度和32位系统下相同类型的长度相同,32位和64位下类型的长度比较见上图的蓝色部分。
下图为在32和64位linux系统下使用sizeof检测出的数据类型的长度。
32位平台下结果:
64位平台下结果:
2 64系统下开发注意事项
2.1 格式化字符串:long使用%ld,指针使用%p,例如:
char *ptr = &something;
printf (%x\n", ptr);
上面的代码在 64 位系统上不正确,只显示低 4 字节的内容。正确的方法是:使用 %p。
char *ptr = &something;
printf (%p\n", ptr);
2.2 数字常量:常量要加L
例1,常数 0xFFFFFFFF 是一个有符号的 long 类型。在 32 位系统上,这会将所有位都置位(每位全为 1),但是在 64 位系统上,只有低 32 位被置位了,结果是这个值是 0x00000000FFFFFFFF。
例2,在下面的代码中,a 的最大值可以是 31。这是因为 1 << a 是 int 类型的。
long l = 1 << a;
要在 64 位系统上进行位移,应使用 1L,如下所示:
long l = 1L << a;
2.3 符号扩展:避免有符号数与无符号数运算,例如:
int i = -2;
unsigned int j = 1;
long l = i + j;
printf("Answer: %ld\n",l);
32位下是-1,在64位下是4294967295。原因在于表达式(i+j)是一个unsigned int
表达式,但把它赋值给k时,符号位没有被扩展。要解决这个问题,两端的操作数只要均为signed或均为unsigned就可。
2.4 转换截断
转换截断发生在把long转换成int时,如下例:
int length = (int) strlen(str);
strlen返回size_t(它在LP64中是unsigned long),当赋值给一个int时,截断是必然发生的。而通常,截断只会在str的长度大于2GB时才会发生,这种情况在程序中一般不会出现。虽然如此,也应该尽量使用适当的多态类型(如size_t、uintptr_t等等)。
2.5 赋值,
不要交换使用 int 和 long 类型,例如:
int i;
time_t l;
i = l;
不要使用 int 类型来存储指针,例如:
unsigned int i, *ptr;
i = (unsigned) ptr;
不要使用指针来存放 int 类型的值。例如:
int *ptr;
int i;
ptr = (int *) i;
2.6 移植倒64位环境下的性能
移植到64位平台后,性能实际上降低了。原因是64位中的指针长度和数据大小有关,并由此引发的缓存命中率降低、数据对齐等问题。通过改变结构中数据排列的先后顺序,会因为少了填充数据,存储空间也随之减少
2.7 程序中链接到的库要使用64位的库。
由上可见所有的问题都是由long和指针长度改变引起,在开发过程中只有牢记long和指针类型的长度。