初识C语言·数据存储

1 整数在内存中的存储

前面讲到,整数在计算机中的存储是以补码形式存储的,其中正数和负数也有些许差别,正数的三码相同,负数的就不相同了,那么这里就涉及原码反码补码。

原码:直接把整数用二进制的方式表达出来的就是原码。

反码:原码除了符号位不变,数值位按位取反就是反码。

补码:反码加1。

对于整型来说,计算机存储的一律是补码,这是因为使用补码可以把符号位和数值位一并处理了。

同时,加法和减法也可以统⼀处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是 相同的,不需要额外的硬件电路。

相互转化我们可以这样理解,比如我们计算3-5,也就是3+(-5)

int main()
{
	//计算3 +  -5
	//3的原码00000000000000000000000000000011
	//-5的原码10000000000000000000000000000101
	//-5的反码11111111111111111111111111111010
	//-5的补码11111111111111111111111111111011
	//补码相加之后
	//补码11111111111111111111111111111110
	//反码11111111111111111111111111111101
	//原码10000000000000000000000000000010
//结果即是-2
	return 0;
}

所以补码和原码相互转化,cpu只需要一个加法器就可以解决完了,不需要额外的减法器之类的。

小tips:补码取反加1就可以直接得到原码,不用挨个挨个倒退回去。


2 大小端字节序和字节序判断

整数在计算机的存储我们现在是了解了,现在我们来看具体细节,均以VS2022为例进行操作。

int main()
{
	int a = 0x11223344;
	return 0;
}

定义一个整型,给它16进制的数字11223344,可能没接触过计算机的人就会认为存进去的时候就是按照从左往右的顺序进行存储的,但是实际上并非如此,由内存中我们可以看到存储是从右往左存储的,所以现在了解什么是大小端字节序(讨论超过一个字节的数据存储)。

大小端:

大端存储模式:是指数据的低位字节内容保存在内存的⾼地址处,⽽数据的⾼位字节内容,保存 在内存的低地址处。

小端存储模式:是指数据的低位字节内容保存在内存的低地址处,⽽数据的⾼位字节内容,保存 在内存的⾼地址处。

像a的11223344,其中11就是高位字节,44就是低位字节,从1到4也就是从高到低,在VS2022中11高位字节存储在高地址处,所以vs2022是小端机器。

那如果让你判断一下该机器是大小端机器的话?如何操作呢?

int main()
{
	int i = 0x1;
	char* pi = &i;
	if (1 == *pi)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
	return 0;
}

如上,我们只需要一个字节的内容,最开始的字节内容或者最后的字节内容就行,所以选择用char类型的指针来判断,如果解引用之后是高字节序的1在高地址处,那么就是小端存储,这是利用指针进行判断的,我们也可以使用联合体进行判断。

union Lab
{
	int a;
	char b;
}num;
int main()
{
	num.a = 1;
	if (1 == num.b)
	{
		printf("小端");
	}
	else
	{
		printf("大端");
	}
	return 0;
}

因为联合体里面的元素是共用空间的,所以我们创建一个整型,一个字符类型,使整型取1,接着就是判断第一个空间是不是1就行了。


3 整数存储练习题

代码1:

int main()
{
	char a = -1;
	signed char b = -1;
	unsigned char c = -1;
	printf("a=%d,b=%d,c=%d", a, b, c);
	return 0;
}

问:代码的运行结果是什么?

在vs里面char ,signed  char类型是一样的所以两个的结果是一样的,因为-1的补码是32个1,截断8个bit位之后,就是11111111,进行整型提升,对符号位进行提升,最后也是32个1,所以a b的结果都是-1,那么,unsigned char类型的没有符号位,全是数值位,截断之后是11111111,提升之后是32个1,32个1也就是2^8 -  1,所以c是255。

代码2:

int main()
{
	char a = 128;
	printf("%u\n", a);
	return 0;
}

首先我们要知道char类型的数据(有符号的)的数值范围是-128——127,所以char a = 128是超出了数据范围的,那么a实际的值是-128(可以用%d验证一下),如果a是130,那么用%d打印出来就是-126,所以当数据范围超了之后的值其实就像一个轮盘一样,绕圈圈绕回来的。

既然a的数值是-128,占位符是%u,是无符号整型,所以会进行整型提升,因为-128比较特殊,它的二进制原码是1000 0000,所以整型提升之后,原码就是1111 1111 1111 1111 1111 1111 1000 0000,这就是原码,因为是无符号整型,所以三码是一样的,那么打印出来就是一个很庞大的数值,4294967168。
那么如果a是-128呢?因为截断之后的原码都是1000 0000,整型提升的结果也是一样的,所以最后的结果没有改变,都是4294967168。

代码3:

int main()
{
	char a[1000];
	for (int i = 0; i < 1000; i++)
	{
		a[i] = -1 - i;
	}
	printf("%zd", strlen(a));
	return 0;
}

strlen,就是用来找0的,如果里面有个0,那么就停止了。数值的读入可以认为是一个循环,char类型的循环可以认为是-1 ……-128 127 126 ……0,可以认为是这样的一个循环,所以char a[1000]里面的数值也是这样的,那么从-1 到 0 一共有255个数,所以打印的结果就是255。

代码4:

unsigned char i = 0;// 0 -> 255
int main()
{
	for (i = 0; i <= 255; i++)
	{
		printf("Hello world\n");
	}
	return 0;
}

unsigned char的范围是0 - 255,那可能就容易以为循环次数是256次,就打印256个Hello world,但是实际上并非如此,如果要结束循环,那么i 的值就应该是256,但是i到256的时候,就变成了0,数值循环嘛,所以造成了死循环。

代码5:

int main()
{
	for (unsigned int i = 9; i >= 0; i--)
	{
		printf("%u\n", i);
	}
	return 0;
}

有了代码4的理解,这段代码同理可得是一个死循环,因为unsigned int的值不可能为负数,所以循环会一直走下去。


4 浮点数在内存中的存储

int main()
{
	int n = 9;
	float* pf = (float*)&n;
	printf("n的值为:%d\n", n);
	printf("*pf的值为:%f\n", *pf);
	*pf = 9.0;
	printf("n的值为:%d\n", n);
	printf("*pf的值为:%f\n", *pf);
	return 0;
}

让我们带着问题去解决这个知识点——为什么差别会那么大?

首先我们要知道浮点数在内存中怎么存储的。

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

V = (-1)^S * M * 2^E

其中(-1)^S表示符号位,当S等于1是,V就是负数,S为0时,V就是为整数,这其实和整数存储的符号位是很像的,1是负数,0是整数。M表示有效数字,M是大于等于1,小于2的(有点类似于科学计数法),2^E表示指数位。

举个栗子,十进制的5.0,二进制表示就是101.0,相当于1.01 * 2 ^ 2,按照V的格式,可以得出S = 0,M= 1.01,E= 2,有点像科学计数法吧?指数位的2^2就像是科学计数法里面的10的几次方一样,这就是二进制的科学计数法。十进制的-5.0,S = 1,M = 1. 01,E= 2。

根据IEEE 754规定,对于32位的浮点数,最高位存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M,

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

当然,存储并不是那么简单就完成了的,IEEE 754对于有效数字M和指数E还有一些特别的规定。

因为M>=1&&M<2,也就是说M可以写成1.xxxxxx的形式,其中xxxxxx表示小数部分,IEEE 754规定,在计算机中存储M的时候,默认这个数第一位总是1,因此可以被舍去,只保留小数部分,比如保留1.01的时候,只保留01,等到读取的时候在把第一位加上去,这样做的好处是可以节省1位有效数字,以32位浮点数为例,留给M只有23位,但是舍去1之后,等于可以保留24位有效数字

关于E,首先它是一个无符号整数,这意味着,如果E为8位,它的取值范围为0~255;如果E为11位,它的取值范围为0~2047。但是,我 们知道,科学计数法中的E是可以出现负数的,所以IEEE 754规定,存⼊内存时E的真实值必须再加上 ⼀个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。⽐如,2^10的E是 10,所以保存成32位浮点数时,必须保存成10+127=137,即10001001。

那么中间数存在的意义是什么呢?像前面讲char取值一样,数值取值就是一个循环,加一个中间数就可以表示负数了。

浮点数取的过程还分为3种情况:
E不全为0或全为1,比如表达0.5在内存中的存储,因为0.5的二进制位表达是0.1,所以是1.0*2^(-1),E是126,0111 1110,整数部分是0,补齐就是24个0,所以二进制表示就是

0 0111 1110 0000 0000 0000 0000 0000 0000。

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

E全为1,这时,如果有效数字M全为0,表⽰±⽆穷⼤(正负取决于符号位s)

ok关于浮点数的存储就说到这里,我们来看题目

为什么9用浮点数指针接收的时候,打印出来的结果就是0.000000了,9的二进制序列是

0000 0000 0000 0000 0000 0000 0000 1001

那么把它按照浮点数形式拆分的话,符号位为0,指数为0000 0000,剩下的23为有效数字是000 0000 0000 0000 0000 1001,这是指数全为0的情况, 所以V = (-1)^0 *000 0000 0000 0000 00001001*2^(-126) = 1.001 * 2 ^ (-146),这是一个接近于0的数,所以用十进制小数表示就是0.000000。

那么为什么用整数打印浮点数9.0是1091567616,首先浮点数9.0二进制是1001.0,即是1.001*2^3,所以9.0 = (-1)^0 * (1001) * 2^3,S = 0,M = 00100000000000000000000,E = 130,即使10000010,最后表达出来就是0 10000010 00100000000000000000000,这是个32位的二进制的整数时,解析出来就是1091567616.

Tips : 不是所有的浮点数都可以表达出来的,君可自行实验。

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

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

相关文章

Pandas:Python可视化神器

大家好&#xff0c;数据可视化可以让我们很直观的发现数据中隐藏的规律&#xff0c;察觉到变量之间的互动关系&#xff0c;可以帮助我们更好的给他人解释现象&#xff0c;做到一图胜千文的说明效果。 常见的数据可视化库有: matplotlib 是最常见的2维库&#xff0c;可以算作可…

Codeforces Round 913 (Div. 3)E 不进位各数位和与打表

Problem - E - Codeforces digsum(a)digsum(b)digsum(c)digsum(n) 要点一&#xff1a; 当左边和发生进位&#xff0c;比如56 11&#xff0c;那么数位和会变小。其实下一位就是相加后对9取余&#xff0c;各数位和必定变小的。 要点二&#xff1a; 然后就是组合情况了&#x…

[NAND Flash 5.5] PLC NAND 虽来但远

依公知及经验整理,原创保护,禁止转载。 专栏 《深入理解NAND Flash》 <<<< 返回总目录 <<<< 前言 图片来源: 存储随笔 2022年8月份在美国FMS峰会上,Solidigm公司(前身为Intel NAND部门)展示了全球第一款基于PLC NAND研发的SSD。这也标志着,PLC…

大模型推理优化实践:KV cache 复用与投机采样

作者&#xff1a;米基 一、背景 RTP-LLM 是阿里巴巴大模型预测团队开发的大模型推理加速引擎&#xff0c;作为一个高性能的大模型推理解决方案&#xff0c;它已被广泛应用于阿里内部。该引擎与当前广泛使用的多种主流模型兼容&#xff0c;并通过采用高性能的 CUDA 算子来实现了…

polar CTF 写shell

一、题目 <?php /*PolarD&N CTF*/highlight_file(__FILE__);file_put_contents($_GET[filename],"<?php exit();".$_POST[content]);?>二、解题 payload ?filenamephp://filter/convert.base64-decode/resourceshell.php #<?eval($_POST[1]);…

maven镜像源设置aliyun提升下载速度

一、打开pom.xml project下在添加 <repositories><repository><id>aliyunmaven</id><name>aliyun</name><url>https://maven.aliyun.com/repository/public</url></repository><repository><id>central2&l…

PriorityQueue优先队列使用的注意事项

PriorityQueue只保证队列的头和尾是指定序列的两个端点值&#xff0c;不是给它的元素排序了。 所以在使用的时候直接打印 PriorityQueue &#xff0c;或者用 增强for 遍历出来的数据都不是有序的。正确的遍历方式如下&#xff1a; // 按照排序顺序输出 PriorityQueue 中的元素…

贪心算法(思路)

最近在cf上做了很多贪心的题&#xff0c;写篇博客来总结一下 Problem - C - Codeforces 看第一道题 不难看出&#xff0c;我们需要在数组中找到一段奇偶相间的序列&#xff0c;要使他们的和最大&#xff0c; 在图中我们假设[1,2]和[3,4]是奇偶相间的序列&#xff0c;我们在在…

Asp .Net Core 系列:基于 Swashbuckle.AspNetCore 包 集成 Swagger

什么是 Swagger? Swagger 是一个规范和完整的框架&#xff0c;用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。它提供了一种规范的方式来定义、构建和文档化 RESTful Web 服务&#xff0c;使客户端能够发现和理解各种服务的功能。Swagger 的目标是使部署管理和使用功…

py爬虫入门笔记(request.get的使用)

文章目录 Day11. 了解浏览器开发者工具2. Get请求http://baidu.com3. Post请求https://fanyi.baidu.com/sug4. 肯德基小作业 Day21. 正则表达式2. 使用re模块3. 爬取豆瓣电影Top250的第一页4. 爬取豆瓣电影Top250所有的250部电影信息 Day31. xpath的使用2. 认识下载照片线程池的…

算法通关村第十六关—滑动窗口与堆结合(黄金)

滑动窗口与堆结合 堆与滑动窗口问题的结合 LeetCode239给你一个整数数组nums,有一个大小为k的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的k个数字。滑动窗口每次只向右移动一位&#xff0c;返回滑动窗口中的最大值。  对于最大值、K个最大这种场…

k8s的存储卷(数据卷)

1、存储卷&#xff1a;容器内的目录和宿主机的目录进行挂载 2、容器在系统上的生命周期是短暂的&#xff0c;delete&#xff0c;k8s用控制器创建的pod&#xff0c;delete相当于重启&#xff0c;容器的状态也会恢复到初始状态&#xff0c;一旦回到初始状态&#xff0c;所有的后…

Java环境变量——Windows和Linux配置jdk

本文我主要是介绍jdk的下载方式和在Windows系统下安装配置jdk11&#xff08;压缩包格式&#xff09;&#xff0c;其他格式的jdk以及Linux操作系统上的jdk安装我后续视情况进行更新… JDK的下载 大家可以去官网Java|Oracle下载对应的资源 继续往下翻&#xff0c;就可以看到Jav…

WorkPlus助力企业高效协作的企业级内网即时通讯解决方案

在企业内部&#xff0c;高效沟通和协作是推动工作顺利进行的关键。而企业级内网即时通讯成为了提升内部沟通效率的重要工具。作为一家领先的企业级内网即时通讯解决方案&#xff0c;WorkPlus以其卓越的性能和高安全性&#xff0c;打造了高效沟通协作的新标杆。 为什么选择WorkP…

【web服务搭建实验】之nginx基础学习

目录 一、nginx的简介二、nginx安装实验虚拟主机的配置web服务器的主流实现方式-LAMP和LNMP 一、nginx的简介 Nginx是一款轻量级HTTP服务器&#xff0c;同时也是代理邮箱服务器&#xff0c;具备反向代理&#xff0c;通用代理的功能。支持多个系统&#xff0c;和不同操作系统。…

Java内容

目录 1.命名规范 1.命名规范 2.变量

蓝桥杯省赛无忧 STL 课件18 总结

3226 宝藏排序 II 1624 小蓝吃糖果 2490 小蓝的括号串1 1531快递分拣

测试SpringBoot的时候报错mapper未装载的解决方案:

1.报错信息和截图&#xff1a; org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name com.tang.testspringboot.TestSpringBootApplicationTests: Unsatisfied dependency expressed through field mapper: No qualifying bean o…

室内定位相关中文期刊/学报笔记

这里写目录标题 文章最重要的部分通信学报1. 2023 基于扩散模型的室内定位射频指纹数据增强方法2. 2023 基于 CHAN 的改进卡尔曼滤波室内定位算法3. 2022 基于自适应蝙蝠算法的室内 RFID 定位算法4. 2017 基于核函数特征提取的室内定位算法研究5. 2021 基于CSI张量分解的室内Wi…

Spring MVC的类型转换器(ConversionServiceFactoryBean)

使用场景 在index.jsp里面添加日期类型 <form action"account/saveAccount" method"post">账户名称&#xff1a;<input type"text" name"name"><br/>账户金额&#xff1a;<input type"text" name&qu…