整数以及浮点数在内存中的存储

一.整数在内存当中的存储

数据在内存中是以十六进制补码的形式进行存储的。

原码表示法简单易懂,适用于乘法,但用原码表示的数进行加减运算比较复杂,当两数相加时,如果同号则数值相加,但是进行减法时要先比较绝对值的大小,然后大数减去小数,最后还要给结果选择恰当的符号。

而负数用补码表示,加法运算只需要一个加法器就可以实现了,不用再配减法器,可以将符号位和数值域统一处理,此外补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。

而1个十六进制可以表示4个二进制位,在内存中查看变量时,只用看(32/4)8位二进制代码即可

#include<stdio.h>
int main()
{
	int hh = 15;
	return 0;
}

再来看一下负数在内存中的情况

#include<stdio.h>
int main()
{
	int hh = -15;
	return 0;
}

此时显示的变量hh在内存中的情况为ff ff ff f1

原码hh=-15为10000000 00000000 00000000 00001111  负数最高符号位为1

           反码为11111111    11111111     11111111    11110000

           补码为11111111     11111111     11111111    11110001

按照一个十六进制位等于4个二进制,将补码可转变为 ff  ff ff f1

大小端字序存储

什么是大小端字序存储

大小端字序存储其实就是字节在内存中存储时的存储顺序。

如果数据的低位字节内容保存在内存的高地址处,而高字节内容保存在内存的低地址处,那么就是大端存储模式

如果数据的高位字节内容保存在内存的高地址处,而低字节内容保存在低地址处,那么就是小端存储模式

比如15,它的十六进制补码为00 00 00 0f

而在vs这个编译器中存储的情况是这样的

编译器里默认左边是低地址,右边是高地址,而存储为00 00 00 00 0f,其实是按照低字节存低地址的规则来进行的,所以是小端存储。

代码判断大小端存储

#include<stdio.h>
int check_sys()
{
	int i = 1;
	int ret = (*(char*)&i);//先把i的地址转变为char*,然后解引用会得到内存中存储的十六进制字节序
	return ret;//char *只能一个字节一个字节访问,此时访问的是第一个内存中的字节
}
int main()
{
	int ret = check_sys();
	if (ret == 1)//小端存储低字节存低位
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
	return 0;
}

整型提升

#include<stdio.h>
int main()
{
		unsigned char a = 200;
		unsigned char b = 100;
		unsigned char c = 0;
		c = a + b;
		printf("%d %d", a + b, c);
		return 0;
	
}

为什么这代码结果会不一样呢,这就涉及到了整型提升

C语⾔中整型算术运算总是⾄少以缺省整型类型的精度来进⾏的。
为了获得这个精度,表达式中的字符和短整型操作数在使⽤之前被转换为普通整型,这种转换称为整型提升。

整型提升的意义

表达式的整型运算要在CPU的相应运算器件内执⾏,CPU内整型运算器(ALU)的操作数的字节⻓度⼀
般就是int的字节⻓度,同时也是CPU的通⽤寄存器的⻓度。?
因此,即使两个char类型的相加,在CPU执⾏时实际上也要先转换为CPU内整型操作数的标准⻓
度。
通⽤CPU(general-purpose CPU)是难以直接实现两个8⽐特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种⻓度可能⼩于int⻓度的整型值,都必须先转换为int或unsigned int,然后才能送⼊CPU去执⾏运算。

再回到上面那个代码

c=a+b

a里面放的是200,200的原码本来应该是32位的00000000 00000000 00000000 11001000

 正数原反补码相同都为00000000 00000000 00000000 11001000

放进只有8位的容器char中,发生截断就为1100 1000

b里面放的是100,100的原码本来应该是00000000 00000000 00000000 01100100

 正数原反补码相同都为00000000 00000000 00000000 01100100

放进只有8位的容器char中,发生截断就为0110 0100

b+c直接相加为100101100,这个得到的是补码

此时以%d(32位有符号十进制原码)打印,要先把b+c补码的结果先填满为32位,00000000 00000000 00000001 00101100,然后再转换为原码,此时最高位为0默认为正数,原反补码都相同,所以补码也为00000000 00000000 00000001 00101100,转换为十进制为300,所以最后打印为300

再来考虑c=a+b,八位a和八位b直接相加为100101100,这是九位,要强行放进只有八位的容器c当中,所以会发生截断,最高位1会被截断了。所以c补码最后为00101100

现在要以%d(32位有符号十进制原码)打印,要先把c的补码填满为32位,00000000 00000000 00000000 00101100,正数转原码为000000000 00000000 00000000 00101100,转换为十进制为44,所以结果为44.

有符号的例子

#include<stdio.h>
int main()
{
	char a;
	char b = 1;
	char c = -1;
	a = b + c;
	printf("%d %d", b+c, a);
}

结果是一样的,但是先别急,试着用上面的方法来分析一下

-1的原码为10000000 00000000 00000000 00000001

-1的反码为11111111 11111111 11111111 11111110

-1的补码为11111111 11111111 11111111 11111111

1的原码是00000000 00000000 00000000 00000001

这是32位整型的情况,而char类型只能放8位,所以就会发生截断,char c实际存放的是11111111

1是正数,原反补码都一样,都为00000000 00000000 00000000 00000001

这是32位整型的情况,而char类型只能放8位,所以就会发生截断,char b实际存放的是00000001

b+c直接以十进制原码的情况打印出来,所以b和c都要重新填满32位然后相加,这时候要去考虑有符号或者无符号数的情况:无符号数整型提升(填满32位),高位全补0;有符号数用符号位填满剩下的位数。

b为有符号数,补码正数符号位0填满为00000000 00000000 00000000 00000001

c为有符号负数,补码符号位1填满为11111111 11111111 11111111 11111111

补码直接相加为 100000000 00000000 00000000 00000000,是33位,%d打印要求32位整型打印,所以依旧会截断,最高位1截断了,得00000000 00000000 00000000 00000000,此时这得到的是补码,而此时最高位为0,所以是正数,正数原反补码相同,最后十进制打印就为0;

再来考虑一下a=b+c打印的情况,如上面说的char b实际存的是8位00000001,char c存的是8位11111111,直接b+c为100000000,九位数要放进只有8位的char a容器里,直接最高位截断了,即得a=b+c=00000000

 而此时要把a以十进制原码打印出来,a不足32位,所以要填满32位,a为有符号数,最高符号位位为0,补满32位为00000000 00000000 00000000 00000000,最高位为0默认为正数,所以原码十进制为0,最后打印也为0;

无符号有符号整型提升串用的例子

#include<stdio.h>
int main()
{
	char a = -1;
	signed char b = -1;
	unsigned char c = -1;
	printf("a=%d,b=%d,c=%d", a, b, c);
}

同样的分析方式,a,b为有符号数,c为无符号数,打印结果为有符号十进制整型,%u是打印十进制无符号整型

-1的原码为10000000 00000000 00000000 00000001

-1的反码为11111111 11111111 11111111 11111110

-1的补码为11111111 11111111 11111111 11111111

而char a只能放8位,所以实际char a补码放的是11111111。此时要求输出十二位十进制原码,所以a需要整型提升填满,a是有符号数,此时最高位为1,补码填满为11111111 11111111 11111111 11111111,原码为10000000 00000000 00000000 00000001(符号位不变其余位取反加一),转换成十进制输出就是-1。

b同a一样,也是-1。

而char c补码实际存放的是11111111,c是无符号数,无符号数整型提升填满32位是高位全补0,即为00000000 00000000 00000000 11111111,此时最高位为0,要求打印%d类型的数据,会自动把c的最高位认为符号位,正数原反补码相同,所以原码为00000000 00000000 00000000 11111111,转换成十进制打印就是255.

有符号char和无符号char范围

无符号char最高位为有效位,最高位参与计算,所以范围为0到255(二进制为1111 1111)

也许有人疑惑,1000 0000反码不是1111 1111,加1原码为9位,截断一位应该为1000 0000啊,最高位为-1,结果不应该为-0吗

虽然8位二进制中,存在-0(1000 0000)和0(0000 0000),实际生活中0又没有正负的,-0不也是0吗,但是它却有两种补码表示,。为了将补码与数字一一对应起来,就认为把原码-0的补码强行人为表示为-128,所以八位二进制-128是没有反码和原码的。看见补码1000 0000,不用去按照负数求原码规则取反加一去计算,它直接表示就是-128,这是人为规定的规则

总结有符号char 的范围为-128到127

如果有符号数最大的正数数127(0111 1111)再加上1,会得到-128(1000 0000),而最大的负数-1(1111 1111)再加1其实得到0000 0000,形成一个环形

同样无符号数最大的数255加1就得到最小的数0,其实也是个环形

练习

#include<stdio.h>
int main()
{
	char a = -128;
	printf("%u\n", a);
	return 0;
}

32位-128,原码10000000 00000000 00000000 100000000

反码为11111111 1111111 1111111 01111111

原码为11111111 11111111 11111111 10000000

char a是个8位的容器,32位放进去会截断1000 0000,而接下来要以32无符号整型输出,8位数要重新填满32位,即为11111111 11111111 11111111 10000000,要以无符号数十进制输出,所以最高位1不是符号数,也要参与转换计算。即为4294967168

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

IEEE754标准

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

十进制的浮点数5.0,写成二进制为101.0,相当于1.01 x 2^2

按照上面的格式,可得s=0,m=1.01,E=2;

IEEE754规定
对于32位的浮点数,最⾼的1位存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M

对于64位的浮点数,最⾼的1位存储符号位S,接着的11位存储指数E,剩下的52位存储有效数字M

浮点数存的过程


IEEE754对有效数字M和指数E,还有⼀些特别规定。
前⾯说过M可以写成 1.xxxxxx 的形式,其中 xxxxxx 表⽰⼩数部分。IEEE754规定,在计算机内部保存M时,默认这个数的第⼀位总是1,因此可以被舍去,只保存后⾯的xxxxxx部分。⽐如保存1.01的时候,只保存01,等到读取的时候,再把第⼀位的1加上去。这样做的⽬的,是节省1位有效数字。以32位浮点数为例,留给M只有23位,将第⼀位的1舍去以后,等于可以保存24位有效数字。
⾄于指数E,情况就⽐较复杂⾸先,E为⼀个⽆符号整数(unsigned int)这意味着,如果E为8位,它的取值范围为0~255;如果E为11位,它的取值范围为0~2047。但是,我们知道,科学计数法中的E是可以出现负数的,所以IEEE754规定,存⼊内存时E的真实值必须再加上⼀个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。⽐如,2^10的E是 10,所以保存成32位浮点数时,必须保存成10+127=137,即10001001。

举个存储的例子

0.5存储,二进制为0.1,规定正数部分必须为1,所以右移操作得1.0*2^(-1),这是正数所以s=0,

E的存储要加127(1023),-1+127=126,二进制表示为0111 1110.

此时M为1.0,按照规则要舍去1,只保留0,而M要存23位,bu'qi为

浮点数取的过程
 

浮点数取的过的过程可以分为三种情况

E的表示不全为0或者不全为1,这也是最常见的情况

这是浮点数取出E的规则是E的计算值减去127(或者1023),就是把前面存的时候加上的中间数减去,还原原数

E全为0的情况

这时,浮点数指数E等于1-127(或者1-1023)即为真实值,有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。这样是为了表示+-0,以及接近0的很小的数字

E全为1的情况

这时,如果有效数字M全为0,表示+-无穷大(正负取决于符号位s)

相关浮点数存储的例题

#include <stdio.h>
int main()
{
	int n = 5;
	float* p= (float*)&n;
	printf("n的值为:%d\n", n);
	printf("p的值为:%f\n", *p);

	*p = 5.0;
	printf("num的值为;%d\n", n);
	printf("*p的值为;%f\n", *p);
}

为什么结果会这么奇怪

首先整数5的二进制型式为 00000000 00000000 00000000 00000101

这时候取地址然后强制转换为float *类型,并用浮点数类型指针p去保存它,此时解引用实际上是把上面那一串二进制数以浮点数的形式取出来

而浮点数形式要考虑占8位的E和占23位的M的值

实质就是以下图V的形式进行输出的

5的32位整数二进制形式 00000000 00000000 00000000 00000101

s是二进制序列中的第一位,所以s=0;

E为00000000 ;

M所属的剩下的23位为00000000 00000000 0000101

还原成V的形式,E在32位电脑下要先减去127,所以指数E为-127

而此时E全为0,所以M的1可以不用加

v=(-1)^0 x 0.00000000 00000000 0000101 x 2^(-127)=(-1)^0 x  1.01 x2^(-148)

此时V是非常接近0的数,极限逼近0,所以输出为0.000000

然后第二环节*p=5.0,是直接把n改写成浮点数的形式,此时要以整数的形式把它输出

浮点数5.0的二进制形式为0101.0,换算成科学计数法形式为1.01 x 2^(2)

按上图理解 s=0,M为1.01,E为2

E占8位,且要先+127,所以E表示为100000001

M的1要省略,有效数字为01,要补满23位,所以M为 01000000 00000000 0000000

写成二进制形式S+E+M形式

0 10000001 01000000 00000000 0000000

转换为十进制为1084227584

总的来说,如果一开始n为整数,而现在要用浮点数形式打印出来,实际是浮点数取的规则

如果一开始就为浮点数,那么以32位整数的形式打印出来,实际使用的是浮点数存的规则

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【面试专题】MySQL篇①

1.MySQL中&#xff0c;如何定位慢查询? ①介绍一下当时产生问题的场景&#xff08;我们当时的一个接口测试的时候非常的慢&#xff0c;压测的结果大概5秒钟&#xff09; ②我们系统中当时采用了运维工具&#xff08; Skywalking &#xff09;&#xff0c;可以监测出哪个接口…

学习pytorch18 pytorch完整的模型训练流程

pytorch完整的模型训练流程 1. 流程1. 整理训练数据 使用CIFAR10数据集2. 搭建网络结构3. 构建损失函数4. 使用优化器5. 训练模型6. 测试数据 计算模型预测正确率7. 保存模型 2. 代码1. model.py2. train.py 3. 结果tensorboard结果以下图片 颜色较浅的线是真实计算的值&#x…

idea使用maven的package打包时提示“找不到符号”或“找不到包”

介绍&#xff1a;由于我们的项目是多模块开发项目&#xff0c;在打包时有些模块内容更新导致其他模块在引用该模块时不能正确引入。 情况一&#xff1a;找不到符号 情况一&#xff1a;找不到包 错误代码部分展示&#xff1a; Failure to find com.xxx.xxxx:xxx:pom:0.5 in …

3D渲染和动画制作软件KeyShot Pro mac附加功能

KeyShot 11 mac是一款专业化实时3D渲染工具&#xff0c;使用它可以简化3d渲染和动画制作流程&#xff0c;并且提供最准确的材质及光线&#xff0c;渲染效果更加真实&#xff0c;KeyShot为您提供了使用 CPU 或 NVIDIA GPU 进行渲染的能力和选择&#xff0c;并能够线性扩展以获得…

HarmonyOS4.0从零开始的开发教程10管理组件状态

HarmonyOS&#xff08;八&#xff09;管理组件状态 概述 在应用中&#xff0c;界面通常都是动态的。如图1所示&#xff0c;在子目标列表中&#xff0c;当用户点击目标一&#xff0c;目标一会呈现展开状态&#xff0c;再次点击目标一&#xff0c;目标一呈现收起状态。界面会根…

Django的logging-日志模块的简单使用方法

扩展阅读&#xff1a; Python-Django的“日志功能-日志模块(logging模块)-日志输出”的功能详解 现在有下面的Python代码&#xff1a; # -*- coding: utf-8 -*-def log_out_test(content_out):print(content_out)content1 "i love you01" log_out_test(content1)现…

<Linux>(极简关键、省时省力)《Linux操作系统原理分析之Linux 设备管理》(29)

《Linux操作系统原理分析之Linux 设备管理》&#xff08;29&#xff09; 10 Linux 设备管理10.1 Linux 设备分类与识别10.1.1 Linux 设备的分类10.1.2 设备文件 10&#xff0e;2 设备驱动程序与设备注册10.2.1 设备驱动程序10.2.2 设备注册 10.3Linux 的 I/O 控制方式10.3.1 查…

Docker, Docker-compose部署Sonarqube

参考文档 镜像地址: https://hub.docker.com/_/sonarqube/tags Docker部署文档地址 Installing from Docker | SonarQube Docs Docker-compose文档部署地址&#xff1a; Installing from Docker | SonarQube Docs 部署镜像 2.1 docker部署 # 宿主机执行 $. vi /etc/sysctl.conf…

探索CSS:从入门到精通Web开发(二)

前言 当我们谈论网页设计和开发时&#xff0c;CSS&#xff08;层叠样式表&#xff09;无疑是其中的重要一环。作为HTML的伴侣&#xff0c;CSS赋予网页以丰富的样式和布局&#xff0c;使得网站看起来更加吸引人并且具备更好的可读性。本书将通过一系列深入浅出的方式&#xff0…

kafka学习笔记--安装部署、简单操作

本文内容来自尚硅谷B站公开教学视频&#xff0c;仅做个人总结、学习、复习使用&#xff0c;任何对此文章的引用&#xff0c;应当说明源出处为尚硅谷&#xff0c;不得用于商业用途。 如有侵权、联系速删 视频教程链接&#xff1a;【尚硅谷】Kafka3.x教程&#xff08;从入门到调优…

深入理解 Promise:前端异步编程的核心概念

深入理解 Promise&#xff1a;前端异步编程的核心概念 本文将帮助您深入理解 Promise&#xff0c;这是前端异步编程的核心概念。通过详细介绍 Promise 的工作原理、常见用法和实际示例&#xff0c;您将学会如何优雅地处理异步操作&#xff0c;并解决回调地狱问题。 异步编程和…

python主流开发工具排名,python开发工具有哪些

本篇文章给大家谈谈python的开发工具软件有哪些&#xff0c;以及python主流开发工具排名&#xff0c;希望对各位有所帮助&#xff0c;不要忘了收藏本站喔。 python中用到哪些软件 一、Python代码编辑器1、sublime Textsublime Text是一款非常流行的代码编辑器&#xff0c;支持P…

基于单片机指纹考勤机控制系统设计

**单片机设计介绍&#xff0c;基于单片机指纹考勤机控制系统设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的指纹考勤机控制系统是一种用于管理员工考勤和实现门禁控制的设计方案。它通过使用单片机作为主控制器…

Amazon CodeWhisperer 提供新的人工智能驱动型代码修复、IaC 支持以及与 Visual Studio 的集成...

Amazon CodeWhisperer 的人工智能&#xff08;AI&#xff09;驱动型代码修复和基础设施即代码&#xff08;IaC&#xff09;支持已正式推出。Amazon CodeWhisperer 是一款用于 IDE 和命令行的人工智能驱动型生产力工具&#xff0c;现已在 Visual Studio 中推出&#xff0c;提供预…

nodejs微信小程序+python+PHP的游戏测评网站设计与实现-计算机毕业设计推荐

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…

初识Matter——esp-box控制两盏灯

初识Matter 一、效果展示 二、准备 1.ubuntu系统/Mac系统电脑 2.安装esp-idf及esp-matter环境 3.esp-box设备 4.两块esp32 5.两个led灯或使用板载灯 三、烧录固件&#xff08;esp-box&#xff09; 下载esp-box例程 git地址&#xff1a;GitHub - espressif/esp-box: Th…

微信小程序 - PC端选择ZIP文件

微信小程序 - PC端选择文件 分享代码片段场景分析解决思路附魔脚本chooseMediaZip 选择附魔后的ZIP文件相关方法测试方法 参考资料 分享代码片段 不想听废话的&#xff0c;直接看代码。 https://developers.weixin.qq.com/s/UL9aojmn7iNU 场景分析 如果你的微信小程序需要选…

机器视觉相机镜头光源选型

镜头选型工具 - HiTools - 海康威视 Hikvisionhttps://www.hikvision.com/cn/support/tools/hitools/cl8a9de13648c56d7f/ 海康机器人-机器视觉产品页杭州海康机器人股份有限公司海康机器人HIKROBOT是面向全球的机器视觉和移动机器人产品及解决方案提供商&#xff0c;业务聚焦于…

dell服务器重启后显示器黑屏

1.硬件层面&#xff1a;观察主机的指示灯 &#xff08;1&#xff09;指示灯偏黄&#xff0c;硬件存在问题&#xff08;内存条有静电&#xff0c;拔出后用橡皮擦擦拭&#xff1b;或GPU松动&#xff09; a.电源指示灯黄&#xff0c;闪烁三下再闪烁一下&#xff0c;扣下主板上的纽…

response应用及重定向和request转发

请求和转发&#xff1a; response说明一、response文件下载二、response验证码实现1.前置知识&#xff1a;2.具体实现&#xff1a;3.知识总结 三、response重定向四、request转发五、重定向和转发的区别 response说明 response是指HttpServletResponse,该响应有很多的应用&…