IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C语言进阶第一问:数据在内存中是如何存储的?(手把手带你深度剖析数据在内卒中的存储,超全解析,码住不亏) -> 正文阅读

[C++知识库]C语言进阶第一问:数据在内存中是如何存储的?(手把手带你深度剖析数据在内卒中的存储,超全解析,码住不亏)

最近博主开学啦!更新节奏有点跟不上,这里做个检讨~

UU们开学感觉怎么样呢?见到同学或者舍友有没有很开心呢?又可以一起愉快地玩耍咯!

但是玩归玩,学习还是正事,话不多说,开始今天的内容。
在这里插入图片描述

在之前的内容中,我们已经把C语言的入门知识进行了一个全面的讲解,并介绍了一些实用的调试技巧,以及函数栈帧的创建和销毁,可以说对于C语言已经算是敲过开门砖了。

那么今天,我们就要开启深入学习C语言的旅程啦!首先解决C语言进阶第一问:数据在内存中是如何存储的?

当然,我们主要探讨的是整型和浮点型这两种类型。

在这里插入图片描述

数据类型

C语言中具以下几种基本内置类型:

在这里插入图片描述

这里说明一下:

  • C语言的基本内置类型只的是C语言本身具有的类型,而库函数本身是不属于C语言的,是独立于C语言之外的。
  • C语言是用来决定语言的语法形式的,而库函数是编译器的产商提供,当然库函数的使用是受C语言标准约束的(比如C语言标准规定了一些库函数的函数名、参数类型、返回值类型和函数功能)。
  • 这样做的好处是虽然不同编译器实现函数的方式不一样,但是对于我们来说,在不同的编译器下使用函数的方式是一样的。
  • 当然在有一些编译器中对一些库函数的支持提供得不是很好,比如在VS编译器下使用scanf函数就可能会报错,需要使用scanf_s。

由于字符类型在存储的时候是按照ASCII码值存储的,所以字符类型也属于整型家族中的一员。

前面我们已经学习了以上这么多的数据类型,并且了解了它们所占内存空间的大小。

我们还要明白,我们为什么要给数据分那么多的类型:

  1. 我们生活中出了的数除了整数就是小数,所以为了更好地存储这两种类型,数据分为整型和浮点型两大类
  2. 使用不同的类型时内存开辟的空间大小是不一样的(使用范围也不一样)。我们应该根据数据的大小选择合适的类型,如果选大了,则浪费空间;选小了,则数据无法完整存储。
  3. 不同类型决定了我们看待空间时应该采用哪种视角。即对于内存中不同空间的类型存和取的方式是不一样的。

在这里插入图片描述

类型的基本归类

整型家族

  • char
    unsigned char
    signed char
  • short
    unsigned short [int]
    signed short [int]
  • int
    unsigned int
    signed int
  • long
    unsigned long [int]
    signed long [int]
    -long long
    unsigned long long [int]
    signed long long [int]

注意:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如何理解有符号和无符号:

以char类型举例:

在这里插入图片描述

这里也说明了不同类型决定了我们看待内存中的值时视角也不同。

同时,我们也可以由此推算出一个类型中最大能放一个多大的数字。

还是以char类型举例:

在这里插入图片描述

由此可以得出,有符号的char中可以存放的数的范围是 -128 ~ 127。

相同道理:
在这里插入图片描述

无符号的char中可以存放的数的范围是 0 ~ 255。

同理,我们可以得出short、int 、long等等类型的范围。

浮点型

float
double

这两种类型通常根据数据要求的精度来选择:精度高选择double类型(8个字节),精度低选择float类型(4个字节)。

一般我们更常选择float类型,但是记住噢~3.14默认是double类型哦!

构造类型

除了内置类型之外,还有构造类型,即可以自己创造的类型。

数组类型
结构体类型 struct
枚举类型 enum
联合类型 union

数组类型为什么也属于构造类型呢?

我们来看看数组的类型:
在这里插入图片描述

在这里插入图片描述
当我们定义的数组元素的类型和个数不同时,数组的类型也不同,可以通过sizeof反映出来,所以我们也说数组属于构造类型。

指针类型

int *pi
char *pc
float *pf
void *pv

空类型

void表示空类型(即无类型)
常用于函数的返回类型、函数参数和指针类型

这里说void作为函数参数是什么情况呢?

正常我们在调用函数的时候,如果这个函数不需要传参,则不写参数。

但是如果是一个不需要传参的函数,而我们又给它传参了,函数会怎样呢?在有的编译器中,会报错,但是有的编译器则会接受这种情况。

但是如果我们给参数加上一个void。编译器就会报错或者警告,告诉你不能传参啦!
在这里插入图片描述
在这里插入图片描述

整型在内存中的存储

我们知道,一个变量创建之后是要在内存中开辟空间的,那么这些变量在内存中到底是怎么存储的呢?

接下来我们就来看看~

首先看看整型在内存中是如何存储的。

我们知道,二进制中有符号的整数有原码、反码、补码三种表示方式。

这三种表示方式都有符号位和数值位两部分。

  • 在符号位中,0表示“正”,1表示“负”。
  • 在数值位中,正数的原、反、补码相同;负数的三种表示方法各不相同:
    原码:直接将二进制按照正负数的形式翻译成二进制。
    反码:将原码的符号位不变,其他位依次按位取反。
    补码:反码+1得到补码。

我们知道对于整型来说,内存中存放的是该数的补码。

在计算机系统中,数值一律用补码来表示和存储。

但是存放的是补码,而不是原码或者反码呢?

因为,使用补码可以将符号位和数值位作统一处理。

同时,加法和减法也可以统一处理(CPU只有加法器)。

因此,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。

比如,我们想计算1-1。但是因为CUP只有加法运算,所以我们可以把表达式写成1+(-1)。

在这里插入图片描述
我们可以看到,用原码计算,得不出正确的结果。

但是如果用补码计算,情况就不一样了。

在这里插入图片描述
从上图的计算中,我们可以看出,使用补码可以直接对符号位和数值位同一进行处理。

这里有一个有意思的事情:

我们知道原码、反码和补码都是怎么得到的,那么计算一下我们会发现用同样的方法,我们也可以通过补码得到原码。

在这里插入图片描述
(不信你试着算一下!)

所以,由此我们可以看到用补码来存储的好处。

大小端字节序

定义

在这里插入图片描述
我们看到,a是一个十六进制的数,当我们在内存中查看它的存储情况时,可以发现,它的存放是从低地址开始放44 33 22 11。

对于它们在内存中的存放顺序,其实可以有很多种存放的方式。
在这里插入图片描述

但是如果大家都按照自己的想法来写,则整个存储就会乱套,而且如果我们不按照正常的顺序存储时,取出来要获得原来的数就比较麻烦,所以我们最后决定只以下两种存储顺序。

在这里插入图片描述
注意:这里的存储顺序是以字节为单位来讨论的。因此,也称字节序。

其中,上面的存储顺序称为小端字节序;下面的存储顺序称为大端字节序。

  • 小端字节序存储
    把一个数的低位字节的内容,存储在内存的低地址出,把这个数的高位字节的内容,存储在内存的高地址处。
  • 大端字节序存储
    把一个数的低位字节的内容,存储在内存的高地址出,把这个数的高位字节的内容,存储在内存的低地址处。

在这里插入图片描述

意义

那么,为什么在存储时还要有大小端之分呢?

这是因为在计算机系统中,内存空间的最小单元是一个字节。所以,如果我们存放的变量大小大于一个字节时,我们就不得考虑每个字节应该以何种顺序存放在内存中了。

那么我们当前的编译器是采取大端存储还是小端存储呢?

别着急,回头看看内存中的值的顺序,就能知道啦!

在这里插入图片描述

所以,我们当前编译器采用的是小端字节序。

百度曾经出过一道笔试题,让被试者设计一个程序来判断当前机器的字节序。

那么我们应该如何做呢?

我们用1来进行观察。

在这里插入图片描述
所以,我们只需要拿出第一个字节,看看里面存的是0还是1就知道了。

在这里插入图片描述

当然,我们要实现的是查看一个机器字节序的功能,所以这里我们最好把程序封装成一个函数。

在这里插入图片描述

浮点型在内存中的存储

看完了整数在内存中是怎么存储的,那么浮点型在内存中又是怎么存储的呢?

我们接下来就来看看。

首先我们常见的浮点数有:

小数:3.1425926
科学计数法:1E10(1.0*1010

在浮点型中,我们有float和double两种类型。

而这两种类型的范围,我们用float.h来定义。

如果我们想看整数的最大值(最小值),我们就用INT_MAX(INT_MIN);并在前面包含头文件<limits.h>。

在这里插入图片描述
然后我们就能看到int类型所能存放的最大值啦!

在这里插入图片描述
那么,浮点数所能存放的最大最小值是多少呢?

同理,我们可以用FLT_MAX(或者FLT_MIN、DBL_MAX等),引用头文件<float.h>,然后转到定义。

就能得到他们的精度啦~
在这里插入图片描述

那么在内存中浮点数的存储和整数的存储是一样的吗?

我们可以先通过一段代码来进行验证。

在这里插入图片描述
那么如果浮点型的存储和整型不一样,它又是怎么存储的呢?

根据国际标准IEEE(电气和电子工程协会) 754,任意一个二进制浮点数V可以表示成下面的形式:

  • (-1)S * M * 2E
    (-1)s表示符号位,当S=0,V为正数;当S=1,V为负数。
    M表示有效数字,1≤M<2。
    2E表示指数位。

我们以小数5.5来举例。

在这里插入图片描述
同理,我们可以写出9.0的表示形式:
在这里插入图片描述
我们会发现,其实任何一个浮点数都可以写成这种形式。

那么,只要我们把一个浮点数写成(-1)S * M * 2E这种形式,那么只要我们存储了S、M、E的信息,就能还原出浮点数的值了。

IEEE 754规定: 对于32位的浮点数,最高的1位是符号位S,接着的8位是指数E,剩下的23位为有效数字M。

在这里插入图片描述

对于64位的浮点数,最高的1位为符号位S,接着是11位的指数E,剩下的52位为有效数字M。

在这里插入图片描述

  • 此外,IEEE 754对有效数字M和指数E,还有一些特别规定。

因为1≤M<2 即M可以写成1.XXXXXX 的形式,其中XXXXXX表示小数部分。

IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此这个1和它后面的小数点可以被舍去,只保存后面的XXXXXX部分。
在这里插入图片描述

比如:当我们要保存1.01的时候,只需要保存01,等到读取的时候,再把前面的1.加上去。

这样,我们就节省了1位有效数字。

以32位浮点数(float)为例,留给M只有23位,将第一位的1舍去以后,等于可以保存24位有效数字,这样精度就得到了提高。

  • 那么对于指数E来说,它又是怎么存进去的呢?

我们知道,对于指数E来说,它是可以去负数的,那么对于负数的E在存储的时候,我们又应该如何处理呢?

于是人们又想出了一个办法,不如给这个E加上一个中间数,让E即使是最小的负数,加上这个数之后也不会为负(等于0),那么就不会出现负数的情况了,只要我们取出来的时候再把这个中间数加上就行了。

这样,我们就可以把E作为一个无符号整数了。

如果E为8位,那么它的取值范围就是:0~255,那么它的中间数就是127。

对于11位的E,它的取值范围是0~2047,它的中间数就是1023。

比如:210的E是10,所以保存成32位浮点数时,就保存成10+127=137,即10001001。

以5.5为例,我们可以在内存中看到它的存储。
在这里插入图片描述
并且我们可以发现,对于浮点数,它在内存中的存储也是有大小端的。

以上,我们就知道了浮点数在内存中是怎么存的了。
那么它又是怎么取出来的呢?

按道理,我们是怎么放进去的,就应该是怎么取出来的。但是由于指数E比较特别,所以再取出来时,又有一些特别的规定。

在内存中取出E时,我们有一下三种情况:

E不全为0或不全为1

这时,E是怎么存进来的,我们就按照原路取出来。

即指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第一位的1。

比如: 0.5的二进制形式为0.1,由于规定正数部分必须为1,即将小数点右移1位,则为1.0*2^(-1)。

在这里插入图片描述

同理,反过来,我们可以通过这个二进制序列得到0.5。
在这里插入图片描述

E全为0

当E全为0的时候,说明浮点数的指数E等于-127,而2-127是一个非常非常小的数,已经十分接近于0了。

所以这时候,我们就不再按照原来的计算方式,而是把浮点数的指数E直接记为1-127(或者1-1023), 并且有效数字M不再加上第一位的1,而是还原为0.XXXXXX的小数。这样做是为了表示±0,以及无穷接近于0的很小的数。

E全为1

当E全为1时,则得到的是255,减去127得到的是128,而2128是一个非常大的数。所以这时,如果有效数字M全为0,则该数就表示±无穷大(正负取决于符号位S)

当我们了解完以上浮点型在内存中的存储规则之后,我们就可以理解之前哪个代码打印出来的值了。

在这里插入图片描述

今天的文章就到这里啦!~

你学废了吗?记得点赞收藏加关注,在评论区留下你的脚印~
在这里插入图片描述

关注我!一起精进C语言!

今天的代码都在这里咯!
https://gitee.com/fang-qiuhui/my-code/blob/4ab3bddffe0dc8e520fabbee219aa421f67d6c1b/blog_2021_8_30_data/blog_2021_8_30_data.cpp

在这里插入图片描述

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-09-06 10:59:24  更:2021-09-06 10:59:28 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/27 21:19:00-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计