带你深度剖析数据在内存中的存储(内附重点练习以及练习解析)

前言💜💜💜

这篇文章详细的介绍了有关整型数据在内存中存储的问题,涉及到了计算机硬件组成原理的部分内容,可以当作学习笔记收藏,时不时可以翻看,那么也希望各位看官不要怕看不懂,坚持看下去,我相信你一定会有所收获,如果还有什么问题,欢迎在评论区留言,或者私信我,我会竭尽全力帮大家解决相关问题。

重难点(目录可直接点击跳转)

  1.数据类型的详细介绍

  2.整形在内存中的存储(原码、反码、补码)

  3.大小端字节序的介绍及判断

一、数据类型的详细介绍❤️❤️❤️

  1.整型家族❤️

  2.浮点型家族❤️

  3.构造类型❤️

  4.指针类型❤️

  5.空类型❤️

二、整形在内存中的存储(原码、反码、补码)❤️❤️❤️

  1.原,反,补码的介绍❤️

  2.内存中的存储方式❤️

  3.补码的运算规则❤️

三、大小端字节序的介绍及判断❤️❤️❤️

  1.大小端模式介绍❤️

  2.为什么有大小端模式之分呢❤️

四、相关练习及解析❤️❤️❤️

  1.练习1❤️

  2.练习2❤️

  练习1解析❤️

  练习2解析❤️

五、总结❤️❤️❤️


重难点

1.数据类型的详细介绍

2.整形在内存中的存储(原码、反码、补码)

3.大小端字节序的介绍及判断


接下来,就让我为大家开始介绍!


一、数据类型的详细介绍👇👇👇

在讲之前,我想先解释一点,此篇文章是基于整型类型所述,所以浮点型我会简单介绍一点,并不会展开介绍,如果大家希望浏览浮点型相关内容,不要着急,后面我还会发布有关浮点型存储的文章,欢迎大家来查阅。

1.整型家族

整型家族的所有类型
以上便是C语言中的所有整型类型,那么大家可能对char类型为什么是整型类型有点疑惑,请看下面:

  • 其实char类型,也就是字符类型,在内存中存储时存储的是它的ASCII码值,char类型的本质是一个ASCII码值,也即是说存储的其实是0 - 127的数字,因此也是整型。

那为什么要有signed(有符号数)和unsigned(无符号数)之分呢,我们平时所写的比如char,int到底是signed还是unsigned呢?

  • 其实是这样的,之所以存在计算机,就是为了帮助我们快速解决一些复杂的问题, 也就是我们在短期内无法解决的问题。 在我们的生活中,有正数和负数之分,是因为有些数值只能为正,有些数值还能为负。比如 温度(有正有负)身高体重(只能为正),因此我们为了便于理解,更方便的在计算机中表示这样的数值,于是就创造了signed和unsigned之分。
  • 而我们平时在写代码时,C语言有规定char、int、short等等都等价于有符号的类型,比如 int a = 0 等价于 signed int a = 0, short a = 0 等价于 unsigned short a = 0 ,long和long long也是一样。
    但是有一个特殊的,C语言没有规定的,也就是char类型,C语言并没有明确规定char类型是signed char还是unsigned char类型,这是由我们所使用的编译器决定的,我所使用的VS编译器默认char等价于signed char类型。

2.浮点型家族

C语言中的浮点数类型
以上是C语言中的所有浮点数类型,其中long doubleC99中引入的。

3.构造类型

C语言的自定义类型
构造类型,也称为自定义类型,这是一类C语言提供的根据设计者自己的需求去设计,创造的类型。

4.指针类型

C语言中的指针类型
这是C语言中的指针类型。其中的空类型也就是void*类型,它是一个非常宽容的指针类型,任何指针都可以放进去,但是解引用访问这个值时,该访问几个字节数呢,这是不确定的,因此就存在问题,所以一定要慎用void*类型。

5.空类型

C语言中的空类型,也叫无类型也就是void类型,它通常用于函数的返回类型,函数的参数,指针类型。例如:

  • 1.void fun(){;}就表示此函数执行完之后不用返回值
  • 2.void fun(void){;}就表示此函数不需要参数
  • 3.void* p = NULL这表示一个指针类型,详情参见第四条指针类型或点击此处

二、整型在内存中的存储👇👇👇

那么整型数据在内存中到底是如何存储的呢?
且听我细细道来

1.原,反,补码的介绍

计算机中的整数有三种二进制表示方法,即原码,反码,补码,三种表示方法均有符号位和数值位两部分,符号位都是用0表示正, 1表示负

  • 原码:将数值(真值)转换为二进制得到的即为原码
  • 反码:正数反码与原码相同,负数原码符号位不变,数值位每位取反
  • 补码:正数补码与原码相同,负数补码符号位不变,反码+1

负数的原码,反码和补码详细运算规则如下:
一个int整型是4个字节,1byte (字节)= 8bit(位),故一个int整型就占32位
原码反码补码运算

2.内存中的存储方式

为什么说内存中存放的是补码呢?下面我是用VS编译器为大家演示:(正数的原,反,补码一样,就不举例了)
VS调试
我们发现-10在内存中存放的的确是ff ff ff f6,那么就验证了整型数据在内存中的确是存放补码。那么有朋友就会问了,你说的是ff ff ff f6,可是内存中明明是f6 ff ff ff啊。是啊,为什么会倒着存放这个数据呢,感觉很奇怪,其实这就涉及到大小端存储模式了,在下文我们会讲到,不用担心。

实际上,对于整形来说,确实如此,在计算机系统中,数值一律用补码来表示和存储。原因在于,是用补码,可以将符号位和数值域统一处理; 同时,加法和减法也可以统一处理(计算机的CPU中只有加法器,减法也是转换成加法进行计算),此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。

就比如 int c = 1 - 1;
计算
通过计算,我们发现在计算机中是使用补码进行运算的

3.补码的运算规则

这里我们将为大家再次介绍加补充补码的运算规则

上面我们说过补码是通过原码转换为反码,再将反码转换为补码,那么大家有没有发现,其实可以一步到位,原码不用再转换为反码了

  • 原码转换为补码:符号位不变,其它位每位取反,末位+1

有原码转换为补码,当然就有补码转换为原码了

  • 补码转换为原码:补码先-1,再取反 或者 补码先取反,再+1都可以(建议大家先只理解一种,一种理解了,另外一种也就理解了)
    说点题外话,这些知识其实都是我们的先辈科学家研究出来的,它们之前是不存在的,或者说我们是不知道的,计算机,计算机系统,补码,原码等等,真的是特别的厉害。当然了,这些知识的底层都是数学,所以大家也不用太过于纠结。
    那么言归正传,这里我只是简单的告诉大家方法结论,要再具体就有数学推导,如果大家感兴趣,👉👉点击此处可查看更加详细的介绍

三、大小端字节序介绍及判断👇👇👇

上面我们讲到内存存储的时候,有一个疑问,为什么存储的时候-10也就是十六进制的ff ff ff f6它在内存中是倒着存的,之前说是大小端模式的关系,那么,究竟什么是大小端?为什么会存在大小端模式呢?下面我就带大家去研究这其中的奥秘。

1.大小端模式介绍

我们说,在内存中,一个数值要是超过一个字节,那它要存储在内存中,就会有顺序的问题。
十六进制数字在内存中如何存放
我们说内存中存储数据是以字节为单位存放的,当一个数据大小超过一个字节后,就会有存放顺序,上图中是吧a数据的最低位也就是44存放在低位地址处,图中蓝色框框的就是内存地址,可以看到44存在低地址处,而最高位也就是11存放在高地址处,中间的还是一样按照低位在低地址,高位在高地址的规则存放。
再举一个例子
十六进制数字在内存中的存放
以上所述,得出结论:

大端模式:数据的低位存放在内存的高地址处,数据的高位存放在内存的低地址处
小端模式:数据的低位存放在内存的低地址处,数据的高位存放在内存的高地址处
那么针对这两点,我们发现VS采用的是小端存储模式

也许大家还有一个疑惑,就是ff ff ff f6存放的时候,为什么f6没有改变顺序呢。有这个疑惑很正常,那到底是为什么呢,其实上面已经说过了,说,内存中存放数据是以字节为单位进行存储的,因此,一个字节内部其实就不存在顺序问题了,f6就是f6(一个十六进制位相当于四个二进制位)。一个十六进制位最大数是15,也就是f,翻译成二进制也就是1111,最大用4个bit位来表示,因此f6就是8个bit位,一个字节,因此没有存放顺序的问题

2.为什么有大小端模式之分呢?

  • 了解了上面的内容后,则会个问题就比较简单了。
    其实就是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节8bit。但是在C语言中除了8bit的char之外,还有16bit的short型,32bit的long型(这要看具体的编译器,不同编译器是不一样的),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题,因此就导致了大端存储模式和小端存储模式。我们常用的x86结构是小端模式,而KEIL C51则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。

四、相关练习及解析👇👇👇

在练习之前,还有一点补充知识,如果想略过,点击此处到达练习部分👈👈

补充知识

  • 有符号数和无符号数的取值范围
    有符号数和无符号数char类型的取值范围
    其他的类型我就不一一列举了,详情参考下表
类型名称取值范围
signed char-128~+127
short int-32768~+32767
int-2147483648~+2147483647
long int-2147483648~+2141483647
long long int-9223372036854775808~+9223372036854775807
  • unsigned类型的取值范围就是signed类型的绝对值相加

能仔细看到这里的我相信都很努力,那么下面有几个练习帮助大家来巩固,消化这些知识,最好能自己动手做一做,再翻看解析哦💜

练习1.请分别解释大小端字节序存储模式的概念,请设计一个小程序来判断当前机器的存储模式(这是百度曾经的一道笔试题)

练习2.练习

练习1解析:

  • 概念的答案请参考大小端模式介绍
  • 代码解析
    1.代码思想
    代码思想
    2.代码展示及运行结果展示
#include <stdio.h>

int check_model()
{
	int a = 1;
	//如果返回1那就是小端存储模式
	//如果返回0那就是大端存储模式
	return *(char*)&a;
}

int main()
{
	int ret = check_model();//封装一个函数根据返回值确定大小端
	if (1 == ret)
		printf("小端存储模式\n");
	else
		printf("大端存储模式\n");
	return 0;
}

运行结果展示
在这里插入图片描述

练习2解析

练习二解析


总结👇👇👇

本篇文章到这里就结束了,非常感谢大家的浏览,本文完全可以作为一篇复习笔记,其中的内容还是比较丰富的。如果大家还对文中什么地方不清楚,欢迎大家在评论区留言或者私信我也可!期待大家的反馈!谢谢大家!


评论 31
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值