前面的文章介绍中,只对整数在内存中的存储进行了简单介绍,可是只了解这么一点点是远远不够的呢,这篇文章呢就对数据在内存中的存储的几个重难点进行了详细的介绍哦,希望对爱学习的小伙伴们有所帮助~
目录
一.数据类型
二.大小端介绍
三.整型在内存中的存储
四.浮点数在内存中的存储
一.数据类型
在C语言中基本的内置类型有:
char:字符数据类型 short:短整型 int:长整型 long:长整型
long long:更长的整型 float:单精度浮点数 double:双精度浮点数
那在C语言中有没有字符串类型呢? 在C语言中其实是没有string类型的,因为字符串是通过字符数组来进行存储的。
这些基本的内置类型所占的字节给大家插张表格,大家看吧,这样清晰明了一点~
类型的意义:
(1)避免浪费内存以防止内存不够用;
(2)解决存进去的问题,决定使用某个类型得知道所需要开辟的内存空间的大小;
(3)解决取出来的问题,改变看内存的视角,可以一个一个类型一个类型的看,而不一定一个比特位一个比特位的看。
世间万物都需要分类,当然,这里的类型也是归类的。
类型可以分为:
(1)整型家族:
(2)浮点数家族
float 、 double
(3)构造类型(自定义类型)
数组类型 结构体类型(struct) 枚举类型(enum) 联合类型(union)
小编在网上照了一张构造类型的框架图,大家可以看看哦~
(4)指针类型
int *pi 、char *pc、float *pf 、void *pv
(5)空类型
void 表示空类型(无类型)
通常应用于函数的返回类型,函数的参数,指针类型
二.大小端介绍
为何会有大小端这个术语呢?这其中还是有个小故事的,在《格列佛游记》中有样子有讲到两个国家因为吃鸡蛋是从鸡蛋的大端吃还是从鸡蛋的小端吃而争执,还引起了战争,是不是听到这个故事,感觉没必要呢,想着只要把鸡蛋迟到肚子里就好了呀,哈哈哈,但是,反过来想,要不是这些人拥有一颗好奇心以及坚信自己的观点,我们学到的知识术语为何如此形象呢?
好啦,说了这么多,该进入正题啦~
因为小编在学习C语言的途中,使用的是VS2019,所以,这里我们以VS2019编译器的使用为例,进行下面的介绍。
前面讲的那个小故事是说大小端这个术语的由来,那在学习过程中它的由来是?
(一)引入大小端
作为一名程序员,总不能只会写代码,而不擅长调试吧,那么,在调试的过程中,或许你会发现让你意想不到的结果出现哦,接下来进入主题喽~
看这个运行结果截图,我们可以发现在编译器中的存储方式是和我们读出来的存储方式是相反的,那为什么会出现这种情况?我们该如何理解以及解释这种情况呢?不会是编译器出bug了吧,哈哈哈,这可不敢乱下结论哦,接着看……
(二)大小端是什么?
大端(存储)模式:是指数据的低位保存在内存的高地址中,数据的高位保存在内存的低地址中;
小端(存储)模式:是指数据的低位保存在内存的低地址中,数据的高位保存在内存的低地址中;
通俗地讲:就是倒着存就是小端模式,顺着存,就是大端模式。
那为什么会有大小端呢?
理由:因为在计算机系统中,我们是以字节为单位的,每个地址单元它都对应着一个字节,而我们都知道,一个字节是8bit位,但是在C语言中除了8bit位的char之外,还有16bit位的short,32bit位的long类型(不过这里得看编译器),另外,对于位数大于8位的处理器,比如说16位或者32位的处理器,由于寄存器的宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题,从而就导致了大端存储模式和小端存储模式。
我们知道,存储方式有大端和小端两种方式,前面也讲了,倒着存就是小端模式,顺着存,就是大端模式。所以,接下来,举个简单的例子给大家看吧,帮助大家理解~
下面这是代码实现~
通过运行结果,我们再次说明了,在vs2019编译器中是以小端模式存储的。
假如深入内存内部又是怎么样的呢?
注意:
(1)因为大小端是把数据放在内存之后才会有的,所以不能直接对变量进行类型强制转换;
(2)char类型只有一个字节,是没有大小端问题的,大小端指的是以字节为单位的顺序;
通过前面的介绍,大家应该对编译器的大小端存储有了一个相对的了解吧,常说眼里过千遍,不如手里过一遍,写代码一样,看别人演示,别人讲是一回事,自己实操是另外一回事,大家可以在自己的编译器上试试,希望关于大小端的介绍可以对大家在学习这部分内容的时候有一定的帮助吆~
三.整型在内存中的存储
C语言都学习到这里啦,想必大家应该知道在C语言中整型是四个字节,也就是32bite吧,在内存中就是一个32位的二进制数字。
那原码,反码,补码分别是什么呢,它们又是如何求的呢?
这三种二进制表示方法,都由符号位和数值位两部分组成,当是正数时,符号位为0;当是负数时,符号位为1;
原码:就是将一个整数转换成二进制的形式,通俗的讲,原码就是一个整数原有的不做任何改变的二进制形式。
反码:在原码的基础上,将原码的符号位不变,其他位按位取反,就可以得到反码。
补码:在反码的基础上,反码加1,就可以得到补码。
举个例子吧:
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main() { int m = 10; //00000000000000000000000000001010 //正数的原码,反码,补码是相同的; int m = -10; //10000000000000000000000000001010 -- 原码 //11111111111111111111111111110101 -- 反码 //11111111111111111111111111110110 -- 补码 return 0; }
在计算机内部,整数的存储采用的是以补码的方式进行存储,那么,当读取整数时,还需要采用逆向转换的思想,通俗的讲,就是要将补码转换成原码。
四.浮点数在内存中的存储
前面讲过整数在内存中的存储方式了,接下来我们就讨论浮点数在内存中又是如何存储的?
(一)浮点数的表示方法
国际标准IEEE规定(电气和电子工程协会)754,任意一个二进制浮点数V可以表示成下面的形式:
a:(-1)^S * M * 2^E
注:当S = 0时,V为正数;
当S = 1时,V为负数;
M表示有效数字;
2^E表示指数;
说明:
(1)这里有效数字范围是[1,2),大家在学习计算机知识的过程中如果对二进制不熟悉,我们就可以拿从小接触到大的十进制进行类比,这样就可以很容易理解啦。
(2)指数部分以2为底数,同样的道理,类比十进制中指数部分是以10为底数的。
举个例子:
(2)浮点数在内存中的存储
根据标准规定:
a:对于32位的浮点数(float型)
最高的一位是符号位S,接下里8位是指数E,剩下的23位为有效数字M。
b:对于64位的浮点数(double型)
最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M。
这里分开做几点说明:
1、符号位S
对于符号位S,只有0和1两种情况,分别表示正和负;
2、有效数字M
对于有效数字M,由于M的范围是[1,2),也就是说M的整数部分一定为1,所以IEEE754标准规定:在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的小数部分。比如保存1.01的时候,只保存小数部分01,而将整数部分的 1会舍去,等到要读取的时候,再把第一位的1加上去就好啦。这么做的目的就是,可以节省1位有效数字。32位浮点数留给M只有23位,将第一位舍去后,就可以保存24位有效数字,可以使数据的精度更高一些。
3.指数部分E(相对复杂)
指数部分E是一个无符号整数(unsigned int)。
如果E为8位(32位浮点型),那么E能表示的范围是0 ~ 255;
如果E为11位(64位浮点型),那么E能表示的范围是0 ~ 2047。
当然,这个指数E也可以为负,但unsigned int的类型使E为非负数,所以IEEE754标准规定,存入内存中时,真实的指数必须加上一个中间值:
单精度:(8位的时候):这个中间数是127;
双精度:(11位的时候):这个中间数是1023;
这是规定,大家没必要再去深究为何是这样哦~
指数在内存中取出还分三种情况:
(1)E不全为0或1
规则:指数E的计算值减去127/1023,得到真实值,然后再将有效数字M前加上第一位的1就好啦
举个列子:
对于浮点数5.0:前面分析了,S= 0,M = 1.01,这里需要将M小数点前面的整数去掉,然后把小数部分的01进行存储,后面不够的位数全部用0补齐,然后E= 2,这使,需要加上+127==>129,然后转换成2进制,再进行存储,所以,5.0表示为2进制为:
0 10000001 0100000000000000000000
(2)E全为0
当E加上后127为全0,那么换个说法就是说E的真实值其实是 -127,即该浮点数指数部分是2 ^ (-127),显然这是一个很小很小的数,那么此时有效数字M不再加上第一位的1,而是还原为以0为整数的小数。这样做是为了表示 ± 0,以及接近于0的很小的数字。
(3)E全为1
当E加上后127为全1,那么换个说法就是说E的真实值其实是 128,即该浮点数指数部分是2 ^ (128),显然这是一个很大很大的数,此时表示正负无穷大(正负号由S决定)。
好啦,关于数据在内存中的存储详细介绍在这里就画上圆满的句号啦,希望对大家能够有帮助,看完的话,动一下可爱的小指头来个小心心吧。如果在阅读的过程中发现错误之处,欢迎大家评论区留言吖~