目录
5.2.1 用%d打印unsigned char a=-1的值
数据类型总体思维图如下:
char本身是字符类型,但是字符在内存中是以ASCII值来存储的,因此也将char划为整形家族
1、计算机是如何分配空间的
假设在32位机器上,有32根地址线,这32根线每根线都会产生不同的高低电平,转换成数字信号即:0、1。那么由0、1的二进制构成的不同二进制序列就可以当作计算机唯一的地址号(观察二进制形式的地址号时因为地址太长太麻烦,所以将二进制的地址号转换为十六进制的形式),因此32根地址线可以产生2^32个不同的地址号,一个地址号可以管理1字节(8bit)内容,2^32次方可以管理4294967296个字节。
4294967296/1024=4194304KB
4194304kb/1024=4096MB
4096/1024=4GB
因此32位机器上可管理的内存大小位4个G。
2、数据在计算机中是以什么形式存储的
数据在计算机中都是以二进制的形式存储的,因为在计算机的世界里只能读懂高电平、低电平这两种状态,用数学的方式来表示就是0和1。而且采用二进制的方式可以让计算机很容易进行逻辑运算,在效率上,稳定性上都有很大的提升。
所以在写程序的时候,给变量赋的每一个值都是用二进制的形式存在计算机中的,因为二进制长度过长,且不方便呈现在屏幕上,因此在vs编译器上把二进制转换成十六进制并呈现出来。 4个bit位转换成1个十六进制数字,因此32个bit位可以用8个十六进制位表示。
初始化一个变量a=0x11223344,因为他的类型为int型。int类型的大小为4字节,所以会向内存中申请4个字节的空间,并且把0x11223344放到这个空间里面,0x是十六进制的写法,因此0x11223344是一个十六进制数。 一个十六进制数=4个bit位,因此11表示的是一个字节的内容 。
从上面的程序图来看,发现11223344在内存中是倒着放的,c84的地址中存放的是44,c85的地址中存放的是33。顺序刚好与初始化时的顺序相反,导致这种情况的原因就是计算机系统中存储分两种模式:大端模式、小端模式。
3、大端存储模式与小端存储模式
了解大小端模式时,要明白二进制中的高低位:
10的二进制位”1000“为低位,因此二进制序列中右边为低位,左边为高位。 则0x11223344中11的二进制序列为高位,44的二进制序列为低位。
而且内存中int类型的变量所申请的空间是由低地址到高地址的:
大端模式规定:将二进制中高位的字节内容存放到内存中的低地址,低位的字节内容存到内存中的高地址称为大端模式。
小端模式规定:将二进制中高位的字节内容存放到内存中的高地址,低位的字节内容存到内存中的低地址称为小端模式。
以上程序将0x11223344中的 11(高位)存到了高地址位,44(低位)存到了低地址位,所以该计算器系统是小端模式。
4、原码、反码、补码
上面说到数据都是以二进制的形式存储的,并且是以补码的形式存储的。一个整数分为原码、反码、补码(且都是以二进制的形式表示)。
原码:作用是计算一个数转换成十进制是多少
反码:原码取反得到反码,作用是原码转换补码中间的步骤
补码:反码+1得到补码,作用是计算机中做的任何运算都是用补码进行的
规定:正数的原、反、补码相同
负数的补码=原码取反+1,原码=补码-1后取反。
4.1 有符号数
那么如何区别正数负数的二进制呢?规定一个有符号数的二进制序列中第一位为符号位,若符号位为1则该数为负数,若为0则为正数(符号位不参与原码补码之间的转换,符号位不参与二进制转换十进制的运算)。比如:
int=-127
原码:10000000 00000000 00000000 0111 1111 根据原码求得-127的值
反码:111111111 111111111 111111111 1000 0000
补码:111111111 111111111 111111111 1000 0001
4.2 无符号数
一个无符号数二进制全部位数都为有效位,意为该数的第一位也要参与二进制转换十进制的计算,比如:
一个无符号数的补码为:111111111 111111111 111111111 10000001
因为无符号数=正数,因此补码等于原码
则他的十进制等于:4294967169
有符号的补码:111111111 111111111 111111111 10000001 结果为-127
无符号的补码:111111111 111111111 111111111 10000001 结果为4294967169
从以上可以看出一个无符号的数比有符号的数多了一个有效位,表现出来的值差距如此之大,所以不论是unsigned int 还是int,他们之间的范围是不一样的。
5、unsigned char与char
因为char类型只能读取1个字节内容,因此其值的范围是有限的。
char的范围:
unsigned char范围:
5.1整型提升
整形提升的概念:在char与short类型操作数在使用前会被转换成int类型,这种转换就叫作整形提升。
发生截断现象后,若后续进行整形提升则根据符号位来决定前面补的位数是1还是0,比如:1000 0001符号位是1,则前面补24个1
如果是一个无符号的数,那么前面只需要补0即可。
当初始化char a=-127时,因为-127是一个整形,这里会把他强行给到char会发现截断现象:然而是小端模式,所以只取32个bit位的最后8位(因为小端模式中最后8个bit是放在低地址下的)
这里总共发生了两次整形提升:第一次是将-127的补码给到char a,第二次是打印%d需要其原码进行打印。只要是需要原码补码之间的转换就需要用到整形提升。
5.2 整型提升与unsigned
5.2.1 用%d打印unsigned char a=-1
%d是以十进制打印有符号的整数
5.2.2 用%u打印char a=-1
%u是以十进制打印有符号的整数
结论:先判断变量a是有符号还是无符号,然后打印时进行整型提升是补1还是补0 ,最后根据以%d还是%u打印,若是用%d打印1111 1111 1111 1111 1111 1111 1111 1111,则需要将其进行取反+1等操作拿到原码再计算原码的十进制。若是用%u打印1111 1111 1111 1111 1111 1111 1111 1111,则%u会将其视为一个无符号的数,因此其补码就是原码直接计算其十进制即可。
6、浮点型在内存中的存储
首先观察以下代码:
从运行结果可以得出一个结论:以整型的形式存放的数,用整型的形式打印出来没问题。但是用浮点型的形式打印出来就出问题了,说明整型和浮点在内存中存储方式不一样。
浮点型在内存中的存储稍微复杂,IEEE规定一个二进制浮点数的表现形式如下:
1、(-1)^S*M*2^E
2、S表示的是这个浮点型是正数还是负数,S=0即正数,S=1即负数
3、M是二进制形式,取值范围:1<=M<2,比如9的二进制=1011.0,那么M=1.011
4、E表示指数位,比如9的二进制=1011.0,用表达式表示为:1.011*2^3,则3就是E的值,表示小数点向左移动了3位(E可以是负数,表示小数点要向右移动)。
举一个简单的浮点数:5.5,那么他的表现形式可以写成:(-1)^0 * 1.1011 * 2^2。
首先先计算5的二进制:101。0.5的二进制该怎么表示呢?我们知道二进制的计算方式是从第一个二进制位*2^0开始的,那么浮点型的二进制计算方式是小数点右边的二进制位从2^-1开始,意味着:0.1(0.1是二进制的形式)的计算方式是1*2^-1,2^-1我们知道是等于1/2^1,因此二进制0.1换成十进制是0.5。则5.5的二进制位为101.1,然后根据M的取值范围1<=M<2,所以小数点要往左边移动2位得到:1.1011 * 2^2。
M=1.1011,E=2。5.5是一个正数,因此S=0。
6.1 浮点型如何存储进内存
浮点数的存储只需要把S M E这三个关键的数据存起来就行,也就是将这三个关键的数据转换成二进制形式存储起来。
IEEE规定:因为M的范围始终是1.xxxx这样的形式,因此可以将小数点前面的1在保存时可以去掉,这样一来就可以多一位的空间用于存放数据,等到读取的时候在把这个1给补上就行。所以M在存储的时候只需要存小数点后面的数据(存到后面没有有效数字时补0)。
IEEE规定E是默认位无符号的数的,我们知道指数也会出现-1的情况,比如0.5就是2^-1求出来的,这时候就需要把指数在存储前进行一个操作:指数的值再加上127(在float类型下)或者加上1023(在double类型下),加上之后的值在换成二进制存到对于的空间里。
这里举一个例子:
因为是小端模式,所以40存在是高地址,从上面程序来看,可以证明浮点型的存储方式的确是这样的形式存储的
6.2 浮点型如何从内存中取出
这里分三种情况:
1、当E不为0或不全为0(绝大多数的情况):E的值需要减去127后得到的才为真实值,再把M的第一位补上1,比如:0 01111110 00000000000000这个二进制转换成浮点数如何转换?
s=0,因此他是一个正数。
E=01111110=126,126-127=-1,因此他的指数是-1
M=00000000000000,前面第一位补1,因此他的有效数字M=1.0
他的浮点型表达式=(-1)^0 * 1.0 * 2^-1 ,我们知道(1.0 * 2^-1)表示的是二进制0.1,为了满足1<=M<2,所以乘上2^-1,让小数点右移了一位得到1.0。因此该浮点表达式的二进制是0.1 = 0.5(十进制形式)。
2、当E全部为0时(这是极少数的情况),意味着E的二进制位都是0,什么数字加上127会让二进制全部为0呢,只有-127+127才会得到0,即1/2^127,因此那也是一个很小的数字。
所以规定这种情况下E直接等于1-127(double类型下:1-1023),且有效数字m的第一位不再加上1,直接用0.xxxxx表示就行,这样就可以表示一个很小趋近于0的数字了。
3、当E全部为1时,这时E的值等于128,因为8个bit位的上限是255,128+127=255,255的二进制位显示的全部都是1,当E的真实值等于128意味着,(-1)^S*M*2^128这个值是一个非常大的值,表示的是±无穷大(根据符号位取决于正负)。
6.3 浮点型与整型对二进制序列的不同理解
在对浮点型在内存中的存储形式有了一定的了解后,回到最开始的代码,去分析这个代码为什么打印出来的结果会不一样,原因就是整型的补码在浮点型看来有不一样的理解:
6.3.1 从浮点型角度看整形的二进制码![](https://i-blog.csdnimg.cn/blog_migrate/20285648c204f0e53400ae89df8cd4e5.png)
6.3.2 从整型角度看浮点型的二进制码![](https://i-blog.csdnimg.cn/blog_migrate/bbd238780ca768f25f0f635a68af4526.png)
结语:
本章数据的存储形式就讲到这里啦,希望可以帮助大家对数据存储这一块有更好的理解,如果有遗漏的部分欢迎大家评论区补充哦,如果本文对你起到了帮助,希望可以点赞👍+关注😎+收藏👌哦!
(👉゚ヮ゚)👉谢谢大家!!👈(゚ヮ゚👈)