目录
1.数据的类型
2.整形在内存中的存贮:原返补码介绍
2.1原反补码
2.2大小端存储
3.大小端字节序
4.浮点数在内存中存贮
1.数据的类型
C语言中数据的类型可以大致分为整型家族,浮点型家族,构造类型,指针类型和空类型
整形家族: char: unsigned char signed char int: unsigned int signed int short: unsigned short signed short long: unsigned long signed long | 浮点型家族: float double | 构造类型: >结构体 struct >枚举类型 enmu >数组 int a[10] >联合体 union |
空类型:void 通常用于函数的返回类型,参数,指针类型 |
在本文章中我们重点讨论的是关于整形和浮点型在内存中如何去存储的问题
2.整形在内存中的存储
我们知道,不同的整形类型计算机会为我们开辟不同大小的存储空间,那么整形变量是如何在这片空间存储的呢
比如说: int a=10 int b=-20 |
2.1原反补码
首先我们需要去了解到,在计算机中我们有三种二进制的表示方式,分别是原码,反码和补码,
三种表示方式都有符号位和数值位组成,负数的符号位为1,正数的符号位为0,正数的原反补码相同
举例子来说明:int a=10 a是一个int型的变量,它在内存中占四个字节,一个字节是八个比特位,下图中一个方格代表一个字节
00000000 | 00000000 | 00000000 | 00001010 |
00000000 | 00000000 | 00000000 | 00001010 |
00000000 | 00000000 | 00000000 | 00001010 |
负数的原反补码
负数的原码是在其相反数的原码的基础上符号位置为1,负数的反码是在原码的符号位不动的情况下其他位取反,补码的计算直接在反码的基础上加1就可以了。
举例子:int b=-20
1000 0000 | 0000 0000 | 0000 0000 | 0001 0100 |
1111 1111 | 1111 1111 | 1111 1111 | 1110 1011 |
1111 1111 | 1111 1111 | 1111 1111 | 1110(e) 1100(c) |
通过观察内存我们可以知道一个信息,在内存中b是以补码的型式去存储的,那么为什么呢?
在计算机中我们是以补码的方式去存储整形变量,原因是因为CPU只有加法器,我们可以通过存储补码的方式而直接使用补码的方式去进行数据的运算而不需要其他额外的硬件。但是观察上述图片,我们还有另外一个问题,就是数据的存储顺序好像和我们想象中的不太一样,这是为什么呢?我们由此引出另外一个话题——大小端存储
3.大小端字节序
大端模式:就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
小端模式:就是低字节排放在内存的低地址端,高位字节排放在内存的高地址端。
举例说明:一个整形变量假设值为0x11223344
为什么会有大小端存储这个问题呢?
这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为8bit。但是在C语言中除了8 bit的char之外,还有16 bit的short型,32 bit的long型(要看具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。
我将在下一篇博客中通过代码来实现如何观察自己的计算机是大端或者小端存储
4.浮点数在内存中的存储
我们先来观察以下的代码的执行结果:
#include<stdio.h>
#include<float.h>
int main()
{
int n = 9;
float* pFloat = (float*)&n;
printf("n的值为:%d\n", n);
printf("*pFloat的值为:%f\n", *pFloat);
*pFloat = 9.0;
printf("num的值为:%d\n", n);
printf("*pFloat的值为:%f\n", *pFloat);
return 0;
}
这个结果可能和大家预想的结果有一定的差距,那么为什么会有这种情况产生呢?
说到浮点数在内存中的存储,我们就要提到IEE754
IEEE754标准提供了如何在计算机内存中,以二进制的方式存储十进制浮点数的具体标准,
IEEE754标准发布于1985年. 包括 javascript, Java, C在内的许多编程语言在实现浮点数时, 都遵循IEEE754标准.
IEEE754的最新标准是IEEE754-2008, 但本篇文章主要参考的是IEEE754-1985, 好在两者相差并不大, 而参照1985的标准可以让我们对一些基础概念有更好的理解
IEEE754提供了四种精度规范, 其中最常用的是 单精度浮点型 和 双精度浮点型 , 但IEEE754并没有规定32位浮点数类型需要叫做 float, 或64位浮点数需要叫做 double. 它只是提供了一些关于如何存储不同精度浮点数的规范和标准. 不过一般情况下, 如果我们提到 float, 其实指的就是IEEE754标准中的32位单精度浮点数. 如果我们提到 double, 其实指的就是IEEE754标准中的64位双精度浮点数
浮点数在内存中的存储方式
浮点数在内存中的存储方式为:符号位+指数+尾数
符号位S:表示浮点数是正数还是负数。0表示正数,1表示负数
指数E:指数部分。类似于科学技术法中的M*10^N中的N,只不过这里是以2为底数而不是10。需要注意的是,这部分中是以2^7-1即127,也即01111111代表2^0,转换时需要根据127作偏移调整。
有效数字M:基数部分。浮点数具体数值的实际表示。
上述表述可能仍然不够清楚,我们来通过图示了解一下,图示中展示的是32位的float类型的图示(1位符号位,8位指数位,23位有效数字位),如果是double类型的话,64位中1位是符号位,指数位增长为11位,其余位数为有效数字的位数
关于符号位S
符号位是图示中的红色部分,与整型在内存中存储一样,0为正数,1为负数
关于有效数字M
1≤M<2 ,也就是说,M可以写成 1.xxxxxx 的形式,其中xxxxxx表示小数部分。
IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分。比如保存1.01的时候,只保存01,等到读取的时候,再把第一位的1加上。这样做的目的,是节省1位有效数字。以32位浮点数为例,留给M只有23位,将第一位的1舍去以后,等于可以保存24位有效数字
关于指数部分的E
至于指数E,情况就比较复杂。
E不全为0或不全为1
这时,浮点数就采用下面的规则表示,即指数E的计算值减去127(float情况下)(或1023(double)情况下),得到真实值,再将有效数字M前加上第一位的1。
E全为0
这时,浮点数的指数E等于1-127(或者1-1023)即为真实值,有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。这样做是为了表示±0,以及接近于0的很小的数字。
E全为1
这时,如果有效数字M全为0,表示±无穷大(正负取决于符号位s);