c语言数据在内存中的存储(详解)

本文详细解释了计算机如何分配内存空间、数据在计算机中的存储形式(如原码、反码、补码和大小端模式),以及浮点型在内存中的存储结构,包括整型提升的概念。通过实例展示了有符号数和无符号数的区别,以及unsignedchar与char类型的关系。
摘要由CSDN通过智能技术生成

目录

1、计算机是如何分配空间的

2、数据在计算机中是以什么形式存储的

3、大端存储模式与小端存储模式

4、原码、反码、补码

4.1 有符号数

4.2 无符号数         

5、unsigned char与char

5.1整型提升

5.2 整型提升与unsigned

5.2.1 用%d打印unsigned char a=-1的值

​编辑

5.2.2 用%u打印char a=-1

​编辑

6、浮点型在内存中的存储

6.1 浮点型如何存储进内存

6.2 浮点型如何从内存中取出 

6.3 浮点型与整型对二进制序列的不同理解

6.3.1 从浮点型角度看整形的二进制码:​编辑

 6.3.2 从整型角度看浮点型的二进制码:​编辑

结语:

数据类型总体思维图如下:

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 从浮点型角度看整形的二进制码
 6.3.2 从整型角度看浮点型的二进制码

        

结语:

        本章数据的存储形式就讲到这里啦,希望可以帮助大家对数据存储这一块有更好的理解,如果有遗漏的部分欢迎大家评论区补充哦,如果本文对你起到了帮助,希望可以点赞👍+关注😎+收藏👌哦!

(👉゚ヮ゚)👉谢谢大家!!👈(゚ヮ゚👈)

  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

安权_code

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值