第十六讲:数据在内存中的存储

news2024/11/20 7:09:34

第十六讲:数据在内存中的存储

  • 1.整数在内存中的存储
    • 1.1存储方式
    • 1.2大小端字节序
    • 1.3大小端字节序排序规则
    • 1.4为什么要有大小端
    • 1.5练习
      • 1.5.1练习1
      • 1.5.2练习2
      • 1.5.3练习3
      • 1.5.4练习4
      • 1.5.5练习5
      • 1.5.6练习6
      • 1.5.7练习7
  • 2.浮点数在内存中的存储
    • 2.1练习
    • 2.2浮点数的存储
    • 2.3浮点数的存储过程
      • 2.3.1符号位的存储
      • 2.3.2对于有效数字M的存储
      • 2.3.3对于指数E的存储
        • 2.3.3.1E不全为0或不全为1
        • 2.3.3.2E全为0
        • 2.3.3.3E全为1
    • 2.4题目解析

这一讲分别介绍了整数和浮点数在内存中的存储方式,以及一些题目的解析

1.整数在内存中的存储

1.1存储方式

数据在内存中的存储都是以其二进制位来表示的,而整数的二进制位的表示方法有三种:原码、反码、补码,在内存中,存储的是正数的补码
正数的原码、反码、补码相同
负数的三种表示方式各不相同

那么为什么整数存储的是补码呢?

1.CPU只有加法器,使用补码能够将字符位和数值位统一处理
2.原码和补码进行转换的过程是相同的,不需要额外的硬件电路便可以实现

1.2大小端字节序

当我们对于一个整数变量进行内存监视时,常常会观察到整数的存放顺序和我们创建的变量值顺序是不同的,例如:
在这里插入图片描述
当我们创建了一个a变量时,它在内存中的存储为(VS编译器):
在这里插入图片描述
可以看见,它是倒着存的,不是正着存的,这就涉及到了整数存储顺序的两种方式:大端存储和小端存储

1.3大小端字节序排序规则

大端排序方式:

低位的字节放在高地址,高位的字节放在低地址

小端排序方式:

低位的字节放在低地址,高位的字节放在高地址

我们画图来解析:
在这里插入图片描述

1.4为什么要有大小端

我们常⽤的 X86 结构是⼩端模式,⽽KEIL C51 则为⼤端模式。很多的ARM,DSP都为⼩端模式。有些ARM处理器还可以由硬件来选择是⼤端模式还是⼩端模式。

但是为什么要有着大小端的存在呢?

1.C语言有着多种类型的数(float、 int、char),对于字节安排的问题,必然要被得到处理
2.不同的硬件设计可能导致不同的存储方式,硬件的设计使用某种存储方式可能会优化性能或简化设计
3.不同的存储方式可能对性能有不同的影响

1.5练习

总结:
1.整数在运算时(整型提升、加法、减法)都是补码在进行运算
2.对于整数的打印,分为两种情况:

1.打印有符号整数,如果需要整形提升,那么补的是符号位,将补码转换成原码,进行计算之后将得出值进行打印
2.打印无符号整数,如果需要整形提升,如果最高位为1,补1,为0,补0,和有符号整数相同,但是计算结果时看的是补码,因为无符号整数的原码、反码、补码相同

1.5.1练习1

//设计⼀个⼩程序来判断当前机器的字节序。
//
//设计思路:
//创建一个a变量,赋值为1,其在内存中的存储应该为00 00 00 01
//①如果为小端存储:存储方式应该为01 00 00 00
//②如果为大端存储:存储方式为00 00 00 01
//分别拿出它们首个字节,如果值为1,就是小端存储,如果值位0,就为大端存储
//
//代码1:
int main2()
{
	int a = 1;

	if (*((char*)&a))  //注意:这里为&a,因为只能将地址强转成(char*)类型的指针,否则可能会出现越界访问
		printf("小端存储\n");
	else
		printf("大端存储\n");
	return 0;
}

//代码2:
int DefA()
{
	int a = 1;
	return *((char*)&a);
}

int main()
{
	int ret = DefA();

	if (ret)
		printf("小端存储\n");
	else
		printf("大端存储\n");
	return 0;
}

1.5.2练习2

//1.5.2练习2
int main()
{
	char a = -1;//对于char类型的变量,它可能时signed char类型,也可能是unsigned char类型,具体取决于编译器,这里是有符号类型
	//char类型为一个字节,此时,-1的2进制表示为10000001,它的补码为:11111111
	//因为-1为整形,所以它的补码结果为:11111111111111111111111111111111,而a为char类型,char类型只能存储11111111,所以对于a:
	//补码:11111111,由于要进行打印,发生整形提升,结果为11111111111111111111111111111111
	//原码:10000000000000000000000000000001
	//所以结果为-1
	signed char b = -1;
	//char类型在此编译器下就是有符号类型的,所以对于有符号类型的char分析和上面一样
	//所以结果为-1
	unsigned char c = -1;
	//对于无符号类型,仍为1个字节,所以a还是11111111
	//整型提升结果为00000000000000000000000011111111,因为对于无符号整形,整形提升加0
	//此时符号位为0,所以原码和补码相同,计算的结果为255

	printf("a=%d,b=%d,c=%d", a, b, c);
	//以%d形式打印,表示打印有符号整数
	return 0;
}

1.5.3练习3

//1.5.3练习3
int main()
{
	char a = -128;
	//-128,原码为10000000000000000000000001000000,反码为11111111111111111111111110111111,补码为11111111111111111111111111000000
	//所以a里存的是11000000
	//要打印的是无符号整形,进行整形提升,结果为11111111111111111111111111000000
	//所以结果为4294967168
	printf("%u\n", a);
	return 0;
}

1.5.4练习4

//1.5.4练习4
int main()
{
	char a = 128;
	//对于128,它的原码为00000000000000000000000010000000
	//反码:01111111111111111111111101111111
	//补码:01111111111111111111111110000000
	//存储到a里,结果为10000000
	//打印无符号整形,整形提升
	//补码:11111111111111111111111110000000
	printf("%u\n", a);
	return 0;
}

但是,看练习4,当我们要将128这个值存到char类型中时,a为10000000,这显然就是-128呀!这是因为char类型的取值范围为-128 - 127,128根本存不下,这时存储遵循一个规律:
在这里插入图片描述
所以我们可以将他们看成一个循环,对于其他类型的整数(float、int)也是如此

1.5.5练习5

//1.5.5练习5
#include <string.h>

int main()
{
	char a[1000];
	int i;

	for (i = 0; i < 1000; i++)
	{
		a[i] = -1 - i;
		//strlen是求字符串长度的函数,遇到\0会停止
		//对于一个char类型的数组,里面放的元素为char类型
		//而我们已经了解到了,char类型的取值范围为-128 - 127
		//所以a数组中放的值只能为:-1 -2 -3 ... -127 -128 127 126 ... 2 1 0这些ASCII码值对应的字符
		//遇到\0停止,所以结果为255
	}

	printf("%zd", strlen(a));
	return 0;
}

1.5.6练习6

//1.5.6练习6
unsigned char i = 0;

int main()
{

	for (i = 0; i <= 255; i++)
	{
		//无符号char类型的取值范围为0-255,所以会一直满足循环条件,会一直循环进行打印
		printf("hello world\n");
	}
	return 0;
}

#include <windows.h>

int main()
{
	unsigned int i;

	for (i = 9; i >= 0; i--)
	{
		//对于无符号int类型,他所有的位都会被当成数值位,所以它不会出现负数的情况
		//所以它会一直满足条件,一直进行打印
		printf("%u\n", i);
		Sleep(100);
	}
	return 0;
}

1.5.7练习7

//1.5.7练习7
//X86环境 ⼩端字节序
int main()
{
	int a[4] = { 1, 2, 3, 4 };
	int* ptr1 = (int*)(&a + 1);
	//&a取出的是整个数组的地址,+1表示紧挨着数组的那块地址,将其强转成int*类型的指针赋给ptr1
	//*(ptr-1)得到的就是4
	int* ptr2 = (int*)((int)a + 1);
	//a表示首元素的地址,将其转换成int类型,表示的是一个数!,+1表示地址加1,直接+1就可以了
	//但是要注意:每一个字节都有一个指针,+1表示的是向后偏移一个字节
	//对于a,在内存中的存储为0x 01 00 00 00 02 00 00 00 ...(因为为小端存储),向后偏移一个字节,就变成了:
	//00 00 00 02 00 00 00
	//对他解引用,访问4个字节,所以找到了00 00 00 02,因为为小端存储,所以结果为02000000

	printf("%x,%x", ptr1[-1], *ptr2);
	return 0;
}

2.浮点数在内存中的存储

2.1练习

浮点数的存储和整形的存储是不一样的,下面我们就通过一个练习来直观地感受一下:

//2.1练习
int main()
{
	int n = 9;

	float* pFloat = (float*)&n;
	printf("n的值为:%d\n", n);//9
	printf("*pFloat的值为:%f\n", *pFloat);//0.000000

	*pFloat = 9.0;
	printf("num的值为:%d\n", n);//1091567616
	printf("*pFloat的值为:%f\n", *pFloat);//9.000000
	return 0;
}

2.2浮点数的存储

既然知道了整形和浮点型的不同,那么浮点数是怎么存储的呢?

根据国际标准IEEE(电气和电子工程协会) 754,任意⼀个⼆进制浮点数V可以表示成下⾯的形式:
在这里插入图片描述
我们通过举例来说:

//2.2浮点数的存储
int main11()
{
	float a = 5.5;
	//我们来探讨5.5在内存中的存储形式:
	//5的二进制表示为101.1,写成科学计数法的形式为1.011 * 10^2
	//所以符号位S为0(因为为整数)
	//指数位E = 2
	//数值位为M = 1.011

	return 0;
}

如果没有看懂,我们通过图像来直观感受:
在这里插入图片描述

我们可以简单理解S、E、M这三个值如上

IEEE 745规定:
1.对于32位的浮点数,最高的一位存储的是符号位,接着八位存储指数E,剩下32位存储有效数字M
2.对于64位的浮点数,最高的一位存储的是符号位,接着十一位存储指数E,剩下52位存储有效数字M

在这里插入图片描述

2.3浮点数的存储过程

2.3.1符号位的存储

符号位的存储只占据一个字节,很简单,是正数就是0,是负数就是1

2.3.2对于有效数字M的存储

其实M的取值范围为1<=M<2,也就是说,M总是可以表示成1…的形式,所以IEEE 754规定,在计算机保存M时,只保存小数点后边的部分,前边的1舍去,等到读取的时候,再将1加上去,这样就节省了一位有效数字,使得精度更高,比如:1.01在进行存储时,只存储01,读取时再将1加上;0.10可以表示成1.0 * 10的负一次幂,所以说它在存储时存储0就可以了,需要注意的是:它要在后边补0,也就是说对于1.1,在存储时存储的是01000000000000000000000

2.3.3对于指数E的存储

对于指数的存储比较复杂,分为三种情况讨论:

2.3.3.1E不全为0或不全为1

因为E的值可能为负数,为了将负数表示出来,我们需要将E的值加上127(在32位机器上,偏移值为127,在64位机器上,偏移值为1023),再将其转换成二进制存储即可,这样即可以通过比较指数的大小来判断两个浮点数的大小关系,同时也可以方便地进行加减乘除等计算操作

这种情况为正常情况,比如0.5的二进制表示形式为0.1,也就是1.0 * 10的负一次幂,在存储数值位时要将数值位的一忽略,所以存储时存储的就是0,补齐23位,也就是00000000000000000000000,指数位值为-1,加上127为126,二进制表示为01111110,符号位为0,所以0.5的二进制表示为:

0 01111110 00000000000000000000000
2.3.3.2E全为0

当E全为0时,指数位的值为1-127(它是规定好的),而且此时数值位在进行复原时,补的不是1了,而是0,此时表示的是一个无限接近于0的一个小数

2.3.3.3E全为1

这时表示的是一个无穷大的数

2.4题目解析

//2.4题目解析
int main()
{
	int n = 9;

	float* pFloat = (float*)&n;
	printf("n的值为:%d\n", n);
	//n本来就是一个int类型的数,进行打印,结果为9
	printf("*pFloat的值为:%f\n", *pFloat);
	//对于9:
	//原码:00000000000000000000000000001001
	//对于一个float类型的数,因为要解引用,拿到的是原码:
	//符号位:0 - 正数
	//数值位:00000000000000000001001 - 0.00000000000000000001001
	//指数位:00000000 - 原码为00000000 - 1-127 = -126
	//所以值为0.00000000000000000001001 * 10的-126次幂
	//他表示0.0000000000000000000...1001是一个很小的数
	//尽管要拿出来,拿出的也只是0.000000,所以结果为0.000000

	*pFloat = 9.0;
	//9的二进制表示1001.0 - 1.001 * 10 ^ 3
	//符号位:0 - 正数
	//数值位:1.001 - 00100000000000000000000 - 注意:要在后边补0
	//指数位:3 + 127 = 130 - 10000010
	//全部 —— 0 10000010 00100000000000000000000 - 1,091,567,616
	printf("num的值为:%d\n", n);
	全部 —— 0 10000010 00100000000000000000000 - 1,091,567,616
	printf("*pFloat的值为:%f\n", *pFloat);
	//直接打印出9.000000即可
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1691859.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

常见的几种数据库通过SQL对表信息进行查询

一、前言 我们查询数据库表的信息&#xff0c;一般都使用界面化的连接工具查看&#xff0c;很少使用SQL语句去查&#xff0c;而且不同的数据库SQL语句又各自有差异。但如果通过代码去获取数据库表的信息&#xff0c;这时就需要通过SQL语句去查了&#xff0c;这个在逆向代码生成…

【案例分享】医疗布草数字化管理系统:聚通宝赋能仟溪信息科技

内容概要 本文介绍了北京聚通宝科技有限公司与河南仟溪信息科技有限公司合作开发的医疗布草数字化管理系统。该系统利用物联网技术实现了医疗布草生产过程的实时监控和数据分析&#xff0c;解决了医疗布草洗涤厂面临的诸多挑战&#xff0c;包括人工记录、生产低效率和缺乏实时…

打造专业级网页排版:全方位解析专业字体家族font-family实践与全球知名字体库导览

CSS中的字体家族&#xff08;font-family&#xff09;属性用于指定文本所使用的字体系列。它允许开发者选择一种或多种字体作为备选&#xff0c;确保在浏览器中以最佳可用字体显示文本。本文将深度解析专业级网页排版中字体家族&#xff08;font-family&#xff09;设置的实践技…

掌握Python基本语法的终极指南【基本语法部分】

一、基本语法部分 1.简单数据类型 1.1字符串类型及操作 字符串访问&#xff1a; 1.索引访问 mystr"Hello world" #索引访问 print(mystr[0]) #H print(mystr[-1]) #d print(mystr[-7]) #o print(mystr[6]) #w 2.切片访问 [头下标&#xff1a;尾下标] &#x…

车灯合面合壳密封使用UV胶的优缺点是什么呢?汽车车灯的灯罩如果破损破裂破洞了要怎么修复?

车灯合面合壳密封使用UV胶的优缺点是什么呢? 车灯合壳密封使用UV胶的优缺点如下&#xff1a; 优点&#xff1a; 快速固化&#xff1a;UV胶通过紫外线照射可以在短时间内迅速固化&#xff0c;大大缩短了车灯制造的工艺流程时间&#xff0c;提高了生产效率。高度透明&#xff…

SVG批量转为pdf超有效的方式!

最近在整理工作&#xff0c;发现ppt里面画的图智能导出svg格式无法导出pdf格式&#xff0c;由于在线的网站会把我的图片搞乱而且不想下载visio&#xff08;会把本地的word搞坏&#xff09;&#xff0c;因此琢磨出这种批量转换的方式。 1. 下载并安装Inkscape 下载链接&#xf…

基于Matlab完整版孤立词识别系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景与意义 孤立词识别是语音识别领域的一个重要分支&#xff0c;其目标是将输入的语音信号转换为计算机可…

成都爱尔眼科医院《中、欧国际近视手术大数据白皮书2.0》解读会圆满举行

2024年5月12日&#xff0c;爱尔眼科联合中国健康促进基金会健康传播与促进专项基金、新华社新媒体中心与中南大学爱尔眼科研究院、爱尔数字眼科研究所重磅发布《中、欧国际近视手术大数据白皮书2.0》。这是继2021、2022年在国内相继发布《国人近视手术白皮书》、《2022中、欧近…

C++笔试强训day32

目录 1.素数回文 2.活动安排 3.合唱团 1.素数回文 链接https://www.nowcoder.com/practice/d638855898fb4d22bc0ae9314fed956f?tpId290&tqId39945&ru/exam/oj 现将其转化为回文数&#xff08;这里用字符串存储比较方便转化&#xff09;&#xff0c;然后判断是否为…

无线网卡有几种接口?怎么给电脑选择一款合适的无线网卡?

前言 这篇文章一共有两个问题&#xff1a; 无线网卡有几种接口 怎么给电脑选择一款合适的无线网卡 目测这一期的文章很长很长&#xff0c;但不水。想要给笔记本或台式机升级无线网卡的小伙伴看过来了&#xff01; 最近有小伙伴问&#xff1a;华硕r555笔记本能不能升级无线…

MySql的环境配置与安装

MySQL 数据库 MySQL是一款关系型数据库 关系型数据库 ​ 基本单位是表,一个表中存储一类信息,表与表之间存在关联关系 sql语言(Structured Query Language) 数据库操作语言也属于一种编程语言,专门用作数据库操作分为三种语言 如下 sql安装使用流程 官网 href https://…

【设计模式】JAVA Design Patterns——Converter(转换器模式)

&#x1f50d;目的 转换器模式的目的是提供相应类型之间双向转换的通用方法&#xff0c;允许进行干净的实现&#xff0c;而类型之间无需相互了解。此外&#xff0c;Converter模式引入了双向集合映射&#xff0c;从而将样板代码减少到最少 &#x1f50d;解释 真实世界例子 在真实…

大众汽车集团CARIAD中国领导团队莅临知迪科技考察交流

5月23日&#xff0c;大众汽车集团旗下软件子公司CARIAD中国领导团队莅临知迪科技参观考察&#xff0c;知迪科技COO尹晓航先生率公司技术代表热情接待。 CARIAD中国一行来宾首先参观了知迪科技数采项目改制车。知迪科技软硬件工程师为考察团领导专家们讲解了知迪智驾数采系统&am…

【vs2022】安装copilot和reshaper

直接安装新版vs 17.10 自带集成的copilot支持安装resharper 可以跳过市场里的reshper安装好后依然可以直接使用vs。 resharper 2024.1.2 市场里还是i老版本&#xff1a; copilot 不兼容,这个是之前市场安装的版本 官方建议用vs intall 安装 安裝 GitHub Copilot GitHub.Co…

MySQL之Schema与数据类型优化(五)

Schema与数据类型优化 特殊类型数据 某些类型的数据并不直接与内置类型一致。低于秒级精度的时间戳就是一个例子。另外一个例子是一个IPv4地址。人们经常使用VARCHAR(15)列存储IP地址。然而&#xff0c;它们实际上是32位无符号整数。不是字符串。用小数点将地址分成四段的表示…

ThreadLocal为什么会导致内存泄漏?

问题引出&#xff1a; ThreadLocal是为了解决什么问题而产生的&#xff1f; ThreadLocal发生内存泄漏的根本原因是什么&#xff1f; 如何避免内存泄漏的发生&#xff1f;定义 为了解决多个线程同时操作程序中的同一个变量而导致的数据不一致性的问题。   假设现在有两个线程A…

ASP+ACCESS教师档案管理系统

3.1 系统功能模块图 3.2 E&#xff0d;R模型图 3.3 系统使用流程图 3.4 各个模块功能简介&#xff1a; 本系统分为五个功能模块&#xff0c;它们分别是教师信息录入模块、教师信息修改模块、教师信息查询模块、教师信息打印模块。 下面分别介绍各个模块的功能用途&#x…

社交媒体数据恢复:soma messenger

步骤1&#xff1a;检查备份文件 首先&#xff0c;我们需要确认您是否已开启Soma Messenger的自动备份功能。若已开启&#xff0c;您可以在备份文件中找到丢失的数据。 步骤2&#xff1a;清除缓存并重启应用 有时候&#xff0c;清除Soma Messenger的缓存文件可以帮助恢复丢失的…

el-table 划入划出方法

<template><div><el-table :data"tableData" style"width: 100%" cell-mouse-enter"handleMouseEnter" cell-mouse-leave"handleMouseLeave"><el-table-column prop"ddd" label"日期2" widt…

Unity Physics入门

概述 在unity中物理属性是非常重要的&#xff0c;它可以模拟真实物理的效果在unity中&#xff0c;其中的组件是非常多的&#xff0c;让我们来学习一下这部分的内容吧。 Unity组件入门篇总目录----------点击导航 Character Controller(角色控制) 说明&#xff1a;组件是Unity提…