为了方便表示和测试,以下用例都是在32位环境下进行的。
整数
在计算机里,正整数、负整数、0都是以补码形式存储的,由于数据类型导致的大小不同,如char型1个字节、int型4个字节等,计算机里会存在截断的现象:
-1在32位环境下被存储为:00000000 0 0000000 00000001 00000000, 十进制为256,超出unsigned char的范围0~255,所以将被截断为00000000,十进制为0,a为0。
由于存储时的截断机制,还会发生一些有趣的现象:
32位环境里
-128原码:10000000 00000000 00000000 10000000
-128补码:11111111 11111111 11111111 10000000
-128减1等于-129,原码为:10000000 00000000 00000000 10000001
补码为:11111111 11111111 11111111 01111111
所以,若将-128减1再赋给char型的数据将会被截断为+127
因此,a永远不会小于-128,下面a的值将会不停在-128~127间循环
char a
for(int i=0; i<1000; i++){
a=-i;// a=i也同样如此}
大小端存储
在计算机里,一个大于一个字节的数据在内存中通常是连续存放的,但存放顺序却有两种方式:
大端存储模式:指低位字节的内容存储在高地址,而高位字节的内容存储在低地址。
小端存储模式:指低位字节的内容存储在低位地址,而高位字节的内容存储在高地址。
小端存储示例:(十六进制0x11223344中44是低位字节,11是高位,如同十进制1122,22是低位字节,11是高位字节)
数据在内存中的大小端存储方式与硬件架构相关,如英特尔和 AMD 的处理器就采用了小端存储。
浮点数的存储方式
在计算机里,浮点数和整数的存储方式是不同的,在实际生活中,浮点数可以用科学计数法表示,如 (-1)x1.2e2 ,表示-0.012,所以国际标准IEEE中规定了任意一个二进制浮点数可以表示为:V = (-1) ^ S* M * 2^E,S控制正负,M表示有效数字,2^E表示指数部分(这里的2^E类似于十进制科学计数法的10^E)。例如:
100.11 ——> 1.0011*2^2 1001.11 ——>1.0012^3
二进制float型以32位存储其中最高1位为符号位(存储S),其后8位存储E(指数部分),接着23位存储M。二进制double型以64位存储其中最高1位为符号位(存储S),其后11位存储E(指数部分),接着52位存储M。
需要注意的是,S的值为1或0,M默认是1点几,所以M只需存储小数部分。E是指数,以无符号整数存储,但E在实际运用里可以为负整数,所以需要一个方法来存储负整数,这个方法就是中间数,float里中间数是127,double里中间数是1023,无论正负,存储E的数据使都需要加上这个中间数再存入内存,比如:存储二进制的float型1011.0——>1.0110*2^(-3),-3+127=124,所以
0 0111 1100 01100000000000000000000
S部分 E部分 M部分
若内存里E数据为127,则实际值为0。
浮点数的取出:1.E不为全0或全1。此时取出的顺序即为存入顺序的倒序
2.E全为0。此时E的值默认为1-127=-126(1-1023=-1022),M为0.xxxx,此时该浮点数为一个接近于0的极小数。
3.E全为1。如果M不全为0,表示一个极大的数字,如果M全为0,表示无穷大。
本文知识点已经全部讲解完毕,接下来是2道示例用于理解上面的知识点:
int a = 5;
float* b = (float*)&a;
printf("%f ", *b);
打印的内容不是预期的5.000000而是0.000000,原因是整数和浮点数的存储方式不同。
正整数a在计算机里以补码 00000000 00000000 00000000 00000101存储,将这串二级制赋给floatb,会将其视为浮点型来解读:
0 0000000 000000000000000000000101
S为0,E全为0,E=1-127=-126,此刻一个接近于0的浮点数赋给了b,b为0。
同样的:
float a = 0.125f;//1.0 -3 0 00111 1100 00000000000000000000000
int* b = (int*)&a;
printf("%d ", *b);
打印结果为1040187392而不是浮点型转化为整型被截断为0。
0 00111 1100 00000000000000000000000 表示0.125
0011 1110 0000 0000 0000 0000 0000 0000 这是b实际存储的补码,数值是1040187392。
我们也可以将其转化为16进制,观察它的存储模式:
0 0111110 0000 0000 0000 0000 0000 0000 二进制
00111110 00000000 00000000 00000000 二进制
3E 00 00 00 16进制
在内存里a以小端模式存储:
完。