在学习学习计算机/编程语言的时候,我们会有一个绕不开的话题:数据的存储。以下小编分为两部分来介绍:整型数据存储、浮点型数据存储。
一、整型数据在内存中的存储
1.我们都知道整型数据在开辟变量空间时需要4byte,那么数据是如何在这4byte的空间中存储的?
首先,我们需要了解三个概念:原码、反码、补码。计算机中的有符号数有上述三种表示方法,这三种表示方法均由符号位和数值位两部分组成。符号位(位于二进制序列的最高位):0表示正数,1表示负数。数值位:除符号位剩下的23个bit位,不同的表示方法该位存在差异。下边结合具体的数据分析 “原反补”。
①原码:将数据的绝对值转化成二进制序列,注:若是整数,最高位则为0;若是负数,最高位则为1。
②反码:将原码的符号位不变,其它位按位取反。
③补码:将反码加1,注:符号位参与运算(通常很少影响到符号位)。
注:对于无符号数和有符号整数,其 “原反补” 相等。
对于整型数据来说:数据在内存中存储的是其补码
例:下边分别对 int a = 10; 和 int b = -10; 这两个变量的 “原反补” 进行演示:
<1>10:原反补:0000 0000 0000 0000 0000 0000 0000 1010
<2>-10:原码:1000 0000 0000 0000 0000 0000 0000 1010
反码:1111 1111 1111 1111 1111 1111 1111 0101
补码:1111 1111 1111 1111 1111 1111 1111 0110
当把数据取出显示时,就涉及到从补码---->反码---->原码的转换。对于正数和无符号数而言,直接将其转化成相应的进制即可;对于负数而言,有两种方式:一是将补码除符号位外其它位取反后加1;二是将补码减一,然后把除符号位外的其它位按位取反。
2.深入理解数据的写入和读取过程
写入:如上所述的 “原反补” 约束的是原始数据!与变量本身无关!写入包含以下过程
①先给变量 a/b 开辟空间
②要将数据转化成对应的二进制,此时转化的过程和目标变量无关!!
③目标数据写到对应的开辟好的空间里
读取:在读取一个变量的时候,该变量的类型决定了我们(打印出来的结果)如何看对该变量内部的二进制序列的含义!!不论你 如何看待二进制序列,二进制序列本身是不发生变化的,但是经过类型解释二进制代表的含义是会发生对应的变化的。这 也就是类型的意义。举个简单的例子:比如H2O是水分子的化学式,但是它的表现形式却有固(冰)液(水)气(蒸汽) 三态,当其所处的环境不同时,则展现出不同的状态,但其本质不会发生变化。
注:数据的截断及隐式类型转换问题
在开始学习编程的时候,可能大多数的伙伴都会经历这样的情况,比如说刚定义好并初始化的变量,打印出来的结果与初始化的值不一样,这是怎么回事呢?
char a = -1;
signed char b = -1;
unsigned char c = -1;
printf("a=%d b=%u c=%d\n", a, b, c);
如上所示的例子,定义的变量的初始化结果都是 -1 ,打印出来的结果却大相径庭。细心的你肯定已经发现了,例子中的变量的类型和打印时的类型都有所差异,这就导致了隐式类型转换问题。
首先,我们要注意C语言的输出类型,如下表展示了各种输出类型。右图为寻常算术转换体系,如果某个操作数的类型在该列表中的排名较低,首先要转换成另一个操作数的类型后执行运算。若发生整形提升,提升时对数据的二进制序列补充符号位(正数和无符号数补充0,有符号负数补充1)。
对于char a = -1; char类型的变量占1byte(8bit),输出为%d,则需要对a的类型进行提升为int,其所占的空间大小为4byte。
a:原码:1000 0001
反码: 1111 1110
补码: 1111 1111
提升后的补码:1111 1111 1111 1111 1111 1111 1111 1111
转换后的原码:1000 0000 0000 0000 0000 0000 0000 0001
最高位为符号位,因此打印出的结果为-1;
b:提升后的补码与a相同,但是其打印时的类型为无符号长整型,其原码与补码相同,因此转换后的数为2^32 - 1。
c(无符号数):补码:“ 1111 1111 ”,这里就体现了前述的“原反补” 约束的是原始数据!与变量本身无关!先将-1的补码转换出来然后写入a的变量空间。
提升后的补码: 0000 0000 0000 0000 0000 0000 1111 1111 (无符号数补充0)
因此点打印的结果是255。
二、浮点型在内存中的存储
对于浮点型而言,其存储时包含三部分:符号位S,指数位E,有效数字M。如下所示为32位、64位浮点数的存储模型。
指数E从内存中取出来,分为以下三种情况:
E不全为0或不全为1:这种情况下,浮点数采用下边的规则表示,及指数E的计算值减去127(或 1023),得到真实值,再将有效数字M前加上第一位的1。比如:0.5的二进制形式为0.1,由于规定正数部分必须为1,即将小数点右移一位,则为1.0*2^(-1),其阶码为-1+127=126,表示为:0111 1110,而尾数1.0去掉正数部分为0,补齐0到23位:000 0000 0000 0000 0000 0000,则其二进制形式表示为:0 0111 1110 000 0000 0000 0000 0000 0000。
E全为0:该情况下,浮点数的指数E=1-127(或1-1023)即为真实值,有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小树。这样做是为了表示正负0,以及接近于0的很小的数字。
E全为1:此情况下,若有效数字M全为0,表示正负无穷大(正负取决于符号位S)。