(int char等)数据类型的存储方式及计算取值(最全、最详细~保姆式讲解)(Windows)
目录
1、写在前面。
2、预备知识。
3、整型类型的存储方式。
4、浮点型类型的存储方式。
1、写在前面。
你想知道为什么有的时候你写的代码会算术溢出、栈溢出吗?
那你知道为什么int float等数据类型是有范围的?范围是多少?又是怎么算的?
别急,等你看完本文,这些问题将会被一一解决
别忘了,如果觉得还不错,记得三连呦👉😊~~~
2、预备知识。
预备知识一:计算机存储数据类型的方式以及基础预备知识
①计算机是通过二进制来存储数据的。
②一个字节等于八个比特位。
③char占一个字节;int占4个字节...
预备知识二:原码、补码和反码。
①计算机存储的数据是补码。也就是说,计算机是以补码的方式来存储数据类型的。
②计算机打印在屏幕上的是原码转换成的(就是屏幕上的数字转换成二进制),在系统中计算时用的是补码。
③我们规定:正数的原码、补码、反码相同;负数的 原码 符号位不变,其他位按位取反(即操作符~)得到反码,反码+1得到补码。(这句话可暂时了解,下文会有讲解)
OK 预备知识到此结束。
3、整型类型的存储方式。
首先,我想问:整形家族都有谁?
答:short/int/long/long long等。
每一种类型里面可以分为有符号数(signed)和无符号数(unsigned)
那么,现在,我们将详细地阐述这些类型是如何在计算机中存储和计算的。
我们以char为例来介绍(因为简单),char懂了其他的也就明白了。
我们知道,char是占1个字节,也就是8个比特位。值0可以表示为
signed char
它的意思就是有符号的数。在signed数里(即signed char),最高位规定为符号位!!!
也正因此,其被成为有符号数。
其中, 0表示正,1表示负。 如图:
对于正数部分:
按照预备知识里面的说法:正数的原码、补码、反码相同。
那么,从 0000 0000 到 0111 1111 都是正数。且原补反码相同。0111 1111转换成10进制为127(我不想算,用的计算器)
所以,char的最大值就是127。因为最高位并不参与计算,只用于判断。
其实正数部分就是这么简单。
那负数部分呢?
如果补码是1000 0001,根据原码符号位不变,其他位按位取反然后+1得到补码可知,则反过来,原码为补码减一再按位取反(符号位不变),得到的原码就是1111 1111,就是 -127 。看不懂?看示意图:
那如果是1000 0000呢?它减一不就得到正数吗?
注意,我们为了知识的完备性,在这里可以理解为我们规定1000 0000为-128,也可以理解为由1000 0001(转换十进制为-127)减1得到(-128)。
不过事实也确实是这样。
在此,再次强调一遍,计算机打印出的是原码的数值(转换成的其他的进制),而在计算机运算、存储是以补码的形式。
至此,相信读者们已经可以举一反三了。我们再举几个栗子:
比如:0011 1010 打印出来是 58
1001 1011(补码)打印出来是 -101
(因为其反码是1001 1010,原码是1110 0101)
我们来画一个图,总结一下:
我们不妨画一个圈,让刚刚所讲的知识形成一个闭环:
(注:展现的都是补码。因为这是在内存中存储的数据)
所以char 的取值为-128到127
unsigned char
它就没有那么复杂了。该怎么算就怎么算,就是说它没有了那个最高为的符号位。比如1010 1010就是170;
所以unsigned char取值是0到 255(即1111 1111)
如果你char看懂了,int 以此类推就可以了。因为char是八个比特位,int是32个比特位,就是在数值上大了一点,其他的一模一样。
好了,我们整形类型的存储到这里就说完了。其实整形还算简单的,我们接下来看浮点型,它和整形是完全不同的存储方式。
4、浮点型类型的存储方式。
对于浮点型,与整形有着完全不同的存储方式。
我们先用一段代码
我们发现d解引用后不是9.000000!!!!!!
这是为什么呢?
这就和浮点型在内存中的存储有关了。看完下面的分析和举例,相信聪明的你一定可以明白。
行了,我们不卖关子了,直接开始。
我们规定,浮点型类型的存储的计算公式为:(-1)^S*M*2^E;
它是以科学计数法的形式来表示的。
一个一个参数来解释:
S可以类比我们之前说整形的那个符号位,要么是0,要么是1;
M表示 大于等于1,小于2的数。(解释一下,我们在10进制里面,使用科学计数法是用一个大于等于1、小于10的数乘以10^n来表示的。类比推理一下,在2进制里面就是一个大于1、小于2的数乘以2的多少次)
按上面一行的叙述可知,E表示次数。
(ps:曾经了解过的朋友可能会说不完全是这样,请继续往下看,下文会说,但我们需要声明的是,我们的这种思考方式是正确的)
我们举一个栗子:
比如5.0,写成二进制位101.0,将其换算成科学计数法的形式,就是1.01*2^2,再添一个符号位,那就是(-1)^0*1.01*2^2 (看着比较难受,我手写出来了。呜呜呜呜~不三连都对不起我)
实际上,也就是可能有的朋友在上文会质疑的地方(在这里,我们来说一下):
其实,IEEE 规定,在计算机存储数据的时候,M的1.xxxxxxx里面的1是不存储的(xxxxxxx表示其小数部分),因为它只能是1,只存储后面的数。这样的目的,是为了节省一个字节的空间。这样一来,实际上就可以到存储24位有效数字了。
比如:上文的1.01实际上存储的是01
对于指数E,IEEE也有规定:
①E为一个无符号整数(unsigned int)。这意味着,如果E为8位,它的取值范围为0~255。
②但是,我们知道,科学计数法中的E是可以出现负数的,所以IEEE 754规定,E的真实值必须再加上一个中间数得到存储值。
③对于8位的E,这个中间数是127。
比如,对于N=M*2^10,这里的E是10,所以保存成32位浮点数时,必须保存成10+127=137,即10001001。
同时,指数E还可以再分成三种情况:
(1)E不全为0或不全为1(指二进制)。浮点数就采用上面的规则表示。
(2)E全为0。这时,E的真实值就是有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。这样做是为了表示±0,以及接近于0的很小的数字。
(3)E全为1。这时,如果有效数字M全为0,表示±无穷大(正负取决于符号位s);如果有效数字M不全为0,表示这个数不是一个数。
另外提一嘴,在32位里,E加的中间数是127,64位字节里(比如double),就是1023。
好了,我们回到我们之前说的那个代码的例子上面来。
就是这个。
当9以整形存储时,为0000 0000 0000 0000 0000 0000 0000 1001
在将其类型转换的时候,按照所说的规则,
输出的数应当为(-1)^0^0.000 0000 0000 0000 0000 1001*2^(0-127),这是很小的一个数字吧,然而因为输出的位数有限,我们在控制台上看到的就是其以十进制打出的前面几位,即0.000000
好了,float的如果懂了,double可以类推,如果是double,E是11,M是52(中间数是1023)
读者如果用兴趣,可以自己在自己的编译器中去查看一下自己定义的变量的内存是否是这样。
最后,如果觉得还不错,记得三联~~~也不枉我码了这么多字,在没有鼠标的情况下赔了这么多图(鼠标好像找不到了~~~~~~)