C++提供的整数类型包括short、int和long,每种类型都分为有符号和无符号两种类型,在笔者另一篇文章中叙述过,在不同的编译器下实现不同,三种类型在内存中所占的大小也不统一。在VC和GCC下,short占2字节,int占4字节,long占4字节,long long占8字节。下面的讲解中都以用得较多的int类型为例。
1. 无符号整数
unsigned int在内存中占4字节,并且所有的32位都用来表示数值,可以表示的范围为0x00000000~0xFFFFFFFF,也就是0~4294967295。可以表示的正数范围为有符号数的两倍,当无符号数不足32位时,高位补0。
计算机存储的单位是字节,这四个字节在内存中如何存放就有两种方法了,也就是常听到的大端和小端的概念了,即big-endian和little-endian(endian意思为字节存储次序)。小端模式即低位数据排在低端,高位数据排在高端,大端即相反。对于0x12345678来说,从内存地址0x40000000开始存,如果是小端模式,则从0x40000000~0x40000003四个地址分别为78、56、34、12,大端模式的话,为12、34、56、78。内存地址是从低到高的,大端就是大的(即高位)在最头面,小端即小的(即低位)在最头面。
int main(void)
{
unsigned int i = 0x12345678;
return 0;
}
查看i的内存空间有如下显示:
可以看到i从0x28ff1c单元开始存放,为小端模式。
2. 有符号整数
有符号整数用最高位表示符号,0为正数,1为负数。因为最高位不能表示数值,所以表示整数的范围为0x80000000~0x7FFFFFFF,即-2147483648~2147483647,正数的表示区间为0x00000000~0x7FFFFFFF,负数的表示区间为0x80000000~0xFFFFFFFF。看到这里,你可能会产生疑惑正数范围还好说,负数就有点看不懂了。下面先来复习一下原码、反码和补码的知识,上面的负数范围就很好理解了。
因为计算机只会做加法,所以需要把减法转换为加法,至于为什么计算机只会做加法,没有查到权威的解释,意思就是所有的运算都转换为加法再计算。
负数在内存中都是以补码形式存放的,补码的规则是对这个数值取反加1。对于任何4字节的数值x,都有x + x(反)= 0xFFFFFFFF,于是x + x(反) + 1 = 0,x = -(x(反) +1)。对于0xFFFFFFFF,取反后为0x00000000,加1再乘以-1后为-1。对于0x80000000,可以表达的意义可以是负数0,也可以是0x80000001减去1,由于0的正负值相等,没有必要再来负数0,所以规定为0x80000001(-2147483647)减去1,这样0x8000000也就成为4字节负数的最小值。这就是为什么有符号整数的取值范围中,负数总比整数多一个最小值的原因。
补码也定义为用0减去这个数的绝对值,即x(绝对值) + x(补码) = 0, 对于-1,补码为0 - 1,即0xFFFFFFFF + 0x00000001 = 0,所以-1的补码为0xFFFFFFFF。
int main(void)
{
int i = -1;
return 0;
}
3. 总结
在对整数的内存进行分析时,只通过内存中的数值是不能够判断是有符号还是无符号数,需要查看指令或者已知函数如何操作此内存地址,根据操作方式或函数相关定义得出该地址的数据类型,如API调用MessageBox,它有4个参数,查看帮助得知,第四个参数为一个无符号整数,从而再进行数值的分析。