1、整型类数据
C语言中的整型类数据都归类在整型家族中,其中包括:char、short、int、long、long long这5个大类,而每个大类中又分为两类signed和unsigned,这些都是C语言中的内置类型。以下重点基于char和int这两种类型的数据进行阐述,其余数据类型以此类推。
A、char:signed char 和unsigned char
首先,就char本身而言,它是signed char还是unsigned char呢?这个其实是与编译器有关的。对于VS编译器,乃至C语言大部分编译器,char都是默认为signed char。
(特别说明,char默认为signed char 这个是编译器决定的,对于C语言自身而言,却是标准未定义的。在这点上,其它类型的整型类数据与char不同,在未标明unsigned的情况下,默认为signed类型,这个是C语言自身定义的)
以下对signed char和unsigned char 分别进行讨论。
a、signed char
signed char 中文名为有符号字符型。
signed char 在内存中大小为1个字节,8个比特位,即八位二进制,其中最高位为符号位--1表示负,0表示正。
整型类数据在内存中存储的是补码,因而下面基于补码来对signed char进行讨论。
signed char的范围是:-128 到127 。为什么呢?
首先,signed char在内存中的大小为8个比特位,我们看一下这八位二进制的情况。
八位二进制:(考虑补码)
00000000/00000001/....../01111111/10000000/10000001/....../11111110/11111111
八位二进制补码总共有这么多种情况,下面进行补码向原码的转换,分为以下三种情况:
1、符号位为0。符号位为0,也就是正整数和0,原反补相同,因而这些二进制的实际范围即0-127
2、符号位为1。符号位为0,也就是负整数,因而将二进制补码转化为原码后,得到的实际范围即-1到-127。
3、10000000。这个二进制补码比较特殊,考虑到符号位为1,如果进行正常的补码向原码转化
,得到的是100000000,共9个比特位,显然溢出了,如果截断的话,得到00000000,实际值为0,这显然与补码为00000000的时候重复了。因而,C语言特别规定,对于signed char,存储补码为10000000时,实际值为-128。
由此,我们得到了signed char的范围:-128至127。
所以,如下的代码便可以得到很好的解释:
我们可以直接基于补码进行理解:
a的补码为01111111
b为a+1,那么直接a的补码加1,得到10000000,所以b就是-128
c为b+1,那么直接b的补码加1,得到10000001,相应原码为11111111,所以c就是-127
d为c-2,那么直接c的补码减2,得到01111111,所以d就是127
注:补码的运算实际上将符号位与数值位的相统一,因而具体运算时不再区分符号位与数值位,统一按数值位进行运算
具体输出结果为:
b、unsigned char
unsigned char在内存中占一个字节,八个比特位,八位二进制均为数值位。因为是无符号类型,或者说理解为正数,所以原反补相同。
考虑八位二进制情况:
00000000/00000001/....../10000000/....../11111110/11111111
原反补相同,因而最终得到的unsigned char的范围为:0-255
此时,如果存储整型类数据大小大于一个字节,则自动低位截断。可见下例:
a的补码为11111111,补码加1后得到100000000,共9个比特位,溢出了,故存储时进行低位截断,因而实际存储到b中的补码为00000000,实际值为0,因而最终的输出结果为0。
B、int
int分为两类:signed int 和unsigned int 。int默认为是signed int。
a、signed int
同signed char相同,signed int 最高位为符号位,整体大小为4个字节,32个比特位,因而数值位共31位。
考虑signed int 的补码二进制情况:
00000000000000000000000000000000
00000000000000000000000000000001
10000000000000000000000000000000
1111111111111111111111111111111111110
1111111111111111111111111111111111111
所以也可分为三种情况:
1、符号位为0。此时即为正整数和0的情况,原反补相同,实际的数值范围为:0至2^31-1
2、10000000000000000000000000000000。C语言规定,此补码对应的实际数值为负的2^31
3、符号位为1.此时即为负整数的情况,补码向原码转化后,得到的实际数值范围为:负的2^31至-1
其中具体的运算与signed char类似,在此不多做赘述。
b、unsigned int
unsigned int 为无符号整型,大小为4个字节,32个比特位均为数值位,且原反补相同。
因此unsigned int的取值范围为:0至2^32-1
2、大小端【字节序】存储
我们知道,整型类数据在内存中存储的是补码,补码是二进制的形式,那么这个补码具体是怎样存储的呢?这样就涉及到大小端字节序存储的问题。
首先,我们要明确一点,因为大小端字节序存储是以1个字节为单位来讨论存储顺序的问题,因而只对内存大小大于等于2个字节的数据有意义,而对于char类型的数据是没有意义的(char类型的数据在内存中只占一个字节,因而无所谓顺序问题)
A、大端【字节序】存储
在这种存储方式下,低二进制位放在高地址处,高二进制位放在低地址处。
以下举个例子:
现在有一个数0x11223344 现在用大端【字节序】存储方式对此数据进行存储
低地址 ————>>> 高地址
11 22 33 44
这就是该数据的存储方式,符合规则:低二进制位放在高地址处,高二进制位放在低地址处。
B、小端【字节序】存储
在这种存储方式下,低二进制位放在低地址处,高二进制位放在高地址处。
VS中,整型类数据的存储方式便是用小端【字节序】存储
我们对这段代码进行调试,并打开内存窗口进行观察
可以清楚地看到,低二进制位放在低地址处,高二进制位放在高地址处
特别注明,内存中存储的补码本质上是二进制的形式,只不过在显示时,为了显示的方便,用十六进制的形式进行显示(4个二进制位等同于1个十六进制位)
当然,我们也可以写一段代码来检验是大端字节序存储还是小端字节序存储。