C语言(数据存储)

        Hi~!这里是奋斗的小羊,很荣幸各位能阅读我的文章,诚请评论指点,欢迎欢迎~~     

                                                💥个人主页:小羊在奋斗

                                                💥所属专栏:C语言   

        本系列文章为个人学习笔记,在这里撰写成文一为巩固知识,二为一些学友们展示一下我的学习过程及理解。文笔、排版拙劣,望见谅。 

                                1、大小端字节序和字节序判断

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

                                                2.1浮点数存的过程

                                                2.2浮点数取的过程

1、大小端字节序和字节序判断

        在 C语言(操作符)1 中,我们介绍了整数在内存中的存储,但是我们只介绍了整数的存储形式,并没有介绍整数是如何存储的,那本节我们就来探讨一下整数在内存中究竟是怎么存储的。

        当我们想把0x11223344这个数存到一个整型变量a中时,因为整型数据的大小是4个字节,以字节为单位存储,那么11占一个字节,22占一个字节,33占一个字节,44占一个字节,假设从低地址向高地址存储,那先存11还是先存44就是一个问题。其实超过一个字节的数据在内存中存储的时候,就有存储顺序的问题,按照不同的存储顺序,我们分为大端字节序存储和小端字节序存储。

        也就是说,大端字节序存储是地址由低到高依次存高位,小端字节序存储是地址由低到高依次存低位。

         我们之前可能会疑惑VS中整数在内存中为什么是反着存的,那这里就给了我们解释,VS所在的当前计算机系统使用的是小端字节序存储,而大多数计算机架构使用的都是小端字节序存储。

        如果我们想知道当前的机器使用的是哪种存储方式,可以写一个简单的判断小程序来实现。当我们在一个整型变量中存一个整数1时,如果当前机器是大端字节序存储,那么它存的就是         00 00 00 01,如果当前机器是小端字节序存储,那么它存的就是 01 00 00 00。既然如此,我们只需要将这个整型变量最小字节中的数据拿出来,看这个最小字节单元中存的数是0还是1,如果是0就是大端字节序存储,反之则为小端字节序存储。

        了解了大小端存储,我们来做一些练习加深理解。

        练习1 

        上面代码的输出结果是什么呢?(其实我们在C语言(操作符)2中的表达式求值部分已经学习过)我们来分析一下:

        我们想将整数-1存入一个字符型变量a中,整型和字符型不兼容,通过C语言(操作符)2的学习我们知道要发生截断,所以只将-1的补码的后8位存到a中,我们又用%d(打印有符号整型)打印字符型变量a,那%d就认为它打印的数是整型,a不是整型所以要发生整型提升,a是char(signed char)类型,整型提升高位补符号位,补完后还是32个1,取反加一得原码又变为了-1,最终结果就打印出了-1,因为在VS中char和signed char是一样的,所以打印b也就同样的道理。

        而我们将整数-1存入一个无符号字符型变量c中,截断只存入-1的补码的后8位,用%d打印需要整型提升,而c是无符号字符型,整型提升高位补0,补完后用%d打印时%d看它就是一个比较大的正数,原反补相同,00000000000000000000000011111111就是255。

        练习2 

        将整数-128(补码:11111111111111111111111110000000)存入(signed)char类型a中,截断后a中存的是10000000,用%u(打印无符号整数)打印需要整型提升,a为signed char所以高位补符号位,补完就是11111111111111111111111110000000,目前还是补码,但是在%u眼中无符号整形原反补相同,所以%u再以原码打印出来就是4294967168。

        将a改为128,结果还是一样的,因为它们截断的结果是一样的。

        至于为什么对a存-128还是128结果是一样的,这里再做一个解释:

        我们很早就知道,(signed)char类型的取值范围是:-128~127 ,但是为什么是这个范围并没有解释。char类型占一个字节也就是8个比特位,最高位当做符号位来看待,就剩下了7位存0、1值。

        在C语言(操作符)2中,我们还画了这样一个图,并做了相应解释。

        127+1在数学上是128,但在char类型中就是-128,因为char类型最大的正数就是127,所以给char类型存128和-128是一样的。其他的类型都是这样的道理。

        练习3 

 

        创建一个字符数组大小1000,向数组中存入-1、-2、-3..,然后strlen求字符数组a的长度。我们知道strlen是求字符串的长度,统计的是\0之前的字符个数,那看来意思就是让我们找字符‘\0’了又因为a是字符数组所以-1、-2、..、-127、-128、127、126、...、2、1、0,0在字符眼中就是\0,所以求的是-1~0的长度,是255。

 

        练习4 

        unsigned char的范围是0~255,所以上面的代码给我们的感觉就是打印255个hello world,但事实真是如此吗?当我们运行起来就会发现程序陷入了死循环。这是因为当i加到256时虽然此时不满足循环了,但是这时候的256只是数学上的256,而在unsigned char眼中255+1就变成了0,所以循环继续。

        上面这个代码也是如此,当i为0时,0-1就变成了2^32-1,因为在unsigned int的世界里没有负数,所以程序陷入了死循环。

        练习5

        创建一个整型数组大小4,&a取出整个数组的地址再+1跳过整个数组指向数组末尾,此时指针(地址)的类型是int (*) [4],再强转为 int * 类型的指针赋给ptr1,ptr1[-1]( *(ptr -1) )就是int *类型的指针-1向后推4个字节指向数组最后一个元素4,再解引用用%x(打印16进制)打印出来就是4。

        数组名a表示数组首元素的地址,强转为int类型的整数再+1,再强转为int *类型的指针,最终的结果就是数组首元素的地址+1(整数+1),注意ptr2是一个整型类型的指针,所以它访问的是4个字节的地址。

 

        我们又知道小端字节序是反着存的,所以ptr2指向的数值就是02 00 00 00,打印出来就是2000000。

        如果我们想把代表16进制的0x也打印出来,可以在%的后面加上#。

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

        2.1浮点数存的过程

        浮点数和整数在内存中的存储有什么区别吗?

        如果用我们学到的整数存储的方法来解读上面的代码,打印出来的结果应该就是9、9.000000、9、9.000000。

        但结果并不是这样,通过观察我们发现,只有当n是整数9时用%d打印的结果和当n是浮点数9.0时用%f打印的结果是我们预料中的,所以可以肯定的是整数和浮点数在内存中的存储是不一样的。

        那浮点数在内存中是怎么存的呢?根据 IEEE 754规定,任意一个二进制浮点数V可以表示为这样的形式:V=(-1)^S*M*2^E。(-1)^S表示符号位,当S=0,V为正数;当S=1,V为负数。M表示有效数字,M是大于等于1小于2的。2^E表示指数位。

        比如十进制的5.5,写成二进制就是101.1,转化的方法是按位权来转化,小数点前面的位权是2的1次方、2的2次方、2的3次方....,小数点后面的位权是2的-1次方、2的-2次方、2的-3次方......。十进制的5.5二进制表示101.1用科学计数法表示就是1.011*2^2,又因为5.5是正数,所以用IEEE 754标准表示就是:(-1)^0*1.011*2^2。其中的S=0,M=1.011,E=2。

        任何一个浮点数都可以用S、M、E这三个值来表示,所以浮点数的存储,存储的就是S、M、E 相关的值

        IEEE 754规定:

        对于32位的浮点数(float),最高的1位存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M。

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

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

        前面说过,1<=M<=2,所以M总是1.xxxxx,IEEE 754规定,在计算机内部保存M时,默认这个数的第一位是1,因此1可以舍去,只保存小数点后面的xxxxx,读取的时候再把第一位的1加上就行。这样做的目的是节省1位有效数字,使存储的精度更高。 

        E作为一个无符号整数,如果E为8位,它的取值范围是0~255;如果E为11位,它的取值范围是0~2047。但是在科学计数法中指数有可能是负数,所以 IEEE 754规定,存入内部的E的真实值必须再加上一个中间数,对8位的E这个中间数是127;对11位的E这个数是1023。比如2^10的E是10,保存成32位的浮点数时,必须保存成137,即10001001。我们也不用担心加上这个中间数后E的值还不能为正数,因为当E非常小时已经无限接近于0了,而float和double也是有精度限度的。

        比如float类型的5.5在内存中的存储就是:

 

        本计算机系统采用的是小端字节序存储。 

        2.2浮点数取的过程

        指数E从内存中取出的过程可分为三种情况:

        E不全为0或不全为1(常规情况)

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

        E全为0(罕见)

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

       E全为1(罕见)

        这时,E的真实值为255-127=128,这又是一个极大的数,大到我们无法想象,所以就不去讨论它了。

        了解清楚了浮点数在内存中的存取后,我们再来分析一下前面的那个代码。

        创建一个整型变量n赋值为9,用%d打印时就按常规打印出整数9。我们再取出n的地址将其强转为float *类型的指针再赋给pfloat,再解引用pfloat用%f打印,9在内存中的补码是:

00000000000000000000000000001001,而pfloat是float *类型的指针,所以pfloat就认为它指向的数是一个浮点数,就会按浮点数存取的规则来读取,在pfloat眼中整数9的补码是这个样子:

 

        S=0,E为全0,那这个值就是一个极小的正数,无限接近于0,而float只能打印小数点后6位,所以打印的结果应该就是0.000000。

        而将9.0存到float *类型的地址中时,9.0用二进制表示为:1001.0,根据 IEEE 754 规定,S=0,M=1.001,E=3,那在内存中的存储就应该是下面这个样子的:

        用%d打印时%d就认为这是一个很大的正数 ,原反补相同。而用%f打印就是正常的9.000000。

 

 

        本节内容并不需要我们死记硬背,只需要知道整数和浮点数在底层是怎么存取的,又有什么差异,当某天我们错用格式符打印不同类型的值时,我们要知道是怎么回事,要会分析,为什么会输出这个值,这个值是随机的还是有它的道理的就行。

        只要我们遵守规则,有符号整数就用%d打印,无符号整数就用%u打印,float类型就用%f打印,double类型就用%lf打印,size_t类型就用%zd打印……就行。

         如果觉得我的文章还不错,请点赞、收藏 + 关注支持一下,我会持续更新更好的文章。  

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

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

相关文章

LeetCode 1075,1103,121,42

1075. 项目员工 I 题目链接 1075. 项目员工 I 要求 查询每一个项目中员工的平均工作年限&#xff0c;精确到小数点后两位。 表 P r o j e c t Project Project表中有project_id和employee_id的字段。 E m p l o y e e Employee Employee表中有employee_id、name和experien…

Linux QT和OpenCV冲突

qt.qpa.plugin: Could not load the Qt platform plugin “xcb” in “$HOME/venv/lib/python3.8/site-packages/cv2/qt/plugins” even though it was found. This application failed to start because no Qt platform plugin could be initialized. Reinstalling the applic…

贪心,CF802B. Heidi and Library

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 Problem - 802A - Codeforces 二、解题报告 1、思路分析 这个题相当于你有一个容量为K的Cache&#xff0c;然后给你一系列访存序列 当访问缺失时你不得不替换掉Cache中某些块 学过操作系统都很熟悉页面置…

anaconda怎么运行python

一、在Anaconda Navigator中运行 首先点击菜单栏中的“开始”&#xff0c;在搜索栏中输入“Anaconda”&#xff0c;然后选择“Anaconda Navigator”。 进入主界面&#xff0c;点击“Spyder”中的“Launch”即可。 然后按F5键运行即可。 二、在Anaconda Prompt中运行 也可以在…

Linux - 高级IO

目录 理解五种IO模型非阻塞IO的设置多路转接之select 实现一个简易的select服务器select服务器的优缺点 多路转接之poll 实现一个简易的poll服务器poll服务器的优缺点 多路转接之epoll epoll原理epoll的优势用epoll实现一个简易的echo服务器 epoll的LT和ET工作模式 什么是LT和…

PasteSpider之阿里云OSS功能的设计初衷

前言 在版本v24.6.2.1之后&#xff0c;有一个菜单"OSS配置"&#xff0c;这个配置是干嘛用的呢&#xff1f; 阿里云OSS&#xff0c;或者说云盘&#xff0c;我觉得也可以当CDN使用&#xff0c;比如我们部署了一个网站&#xff0c;为了减少服务器的承载&#xff0c;可以…

数据结构汇总

等同于&#xff1a; 旋转的时候忽略Nil,选装完再加上。

线性数据结构-链表

链表&#xff1a;线性数据结构&#xff08;一维数组结构&#xff09; 线性的数据结构强调存储和顺序 单向链表&#xff08;也称为单链表&#xff09;是一种简单的数据结构&#xff0c;其中每个节点包含两个部分&#xff1a;一个是数据元素&#xff08;或称为值&#xff09;&am…

好用的linux链接工具

工具下载链接&#xff1a; FinalShell SSH工具,服务器管理,远程桌面加速软件,支持Windows,macOS,Linux,版本4.3.10,更新日期2023.12.31 - FinalShell官网FinalShell是一体化的的服务器,网络管理软件,不仅是ssh客户端,还是功能强大的开发,运维工具,充分满足开发,运维需求.特色功…

引领未来,ArmSoM-Sige5震撼发布:RK3576芯片搭载,多媒体应用新宠

在数字化浪潮的推动下&#xff0c;ArmSoM-Sige5携手Rockchip RK3576第二代8纳米高性能AIOT平台&#xff0c;以颠覆性的性能和多功能性&#xff0c;成为多媒体应用的新宠儿。这一全新产品不仅拥有6 TOPS算力NPU和最大可配16GB大内存&#xff0c;更支持4K视频编解码&#xff0c;具…

Simulink从0搭建模型10-P11 建模练习 搭建简易车辆动力学模型

Simulink从0搭建模型10-P11 建模练习 搭建简易车辆动力学模型 前言参考1. 车辆纵向动力学模型2. 相关参数定义3. 车辆动力学模型搭建&#xff08;简易&#xff09;3.1. 思路3.2. 模型解析3.2.1. 输入扭矩3.2.2. 滚动阻力 Ff 部分3.2.3. 坡度阻力 Fi 部分3.2.4. 加速阻力 Fj 部分…

2024年比较火的桌面便利贴,适合懒人的电脑便签

在2024年的数字化生活中&#xff0c;高效的电脑便签软件成为了许多“懒人”提升生产力的秘密武器。这些软件不仅让信息记录变得轻松快捷&#xff0c;还能帮助用户有效管理时间&#xff0c;减少遗忘&#xff0c;让桌面保持整洁有序。 其中&#xff0c;“好用便签”以简洁的设计…

哈希重要思想续——布隆过滤器

布隆过滤器 一 概念1.1布隆过滤器的提出2.概念 二 模拟实现2.1 三个仿函数setTest 全代码三 实际应用 一 概念 1.1布隆过滤器的提出 我们在使用新闻客户端看新闻时&#xff0c;它会给我们不停地推荐新的内容&#xff0c;它每次推荐时要去重&#xff0c;去掉那些已经看过的内容…

【数学】填不同的自然数 1/9=1/()+1/()+1/()+1/()+1/()

填不同的自然数 1 9 1 ( ) 1 ( ) 1 ( ) 1 ( ) 1 ( ) \frac{1}{9}\frac{1}{(\text{ })}\frac{1}{(\text{ })}\frac{1}{(\text{ })}\frac{1}{(\text{ })}\frac{1}{(\text{ })} 91​( )1​( )1​( )1​( )1​( )1​ 推理&#xff1a; 1 1 − 1 2 1 2 − 1 3 1 3 … − 1…

springboot管理的各依赖版本查看

找一个springboot相关的依赖&#xff0c;比如这里我找mybatis 鼠标点击artifactId名称&#xff0c;图中蓝色字段&#xff0c;跳转到springboot依赖&#xff08;鼠标悬停在上面变成蓝色表示可点击跳转&#xff09;&#xff0c; 点击spring-boot-dependencites&#xff0c;跳转到…

python用tanh画图

用tanh函数画图 圆形 import numpy as np import matplotlib.pyplot as plt# 创建一个二维网格 xx np.linspace(-1, 1, 1000) yy np.linspace(-1, 1, 1000) x_i, y_i np.meshgrid(xx, yy)# 圆的半径和中心 r 0.4 center_x, center_y 0, 0 # 假设圆心在(0, 0)# 计算每个网…

pandas添加行

方法1(df.append()) import pandas as pd# 创建一个空的DataFrame df pd.DataFrame(columns[Column1, Column2])# 新增一行数据 data {Column1: Value1, Column2: Value2} df df.append(data, ignore_indexTrue) print(df)raw_data {"Column1":"adafafa&quo…

探究JSON和XML:两种常见的数据交换格式之异同

在软件开发和数据交换领域&#xff0c;JSON&#xff08;JavaScript Object Notation&#xff09;和XML&#xff08;eXtensible Markup Language&#xff09;是两种广泛使用的数据交换格式。它们都具有将数据结构化并在不同系统之间进行传输和解析的能力&#xff0c;但在实际应用…

详解redis配置文件

华子目录 基础配置查看配置文件位置启动redis时&#xff0c;指定加载的配置文件示例 include引用其他配置文件units单位大小写不敏感network网络配置bind配置方式示例 protected-mode功能概述默认值配置方法安全建议总结 porttcp-backlog1. 定义2. 默认值3. 优化建议4. 注意事项…

OpenEuler 的安装过程记录

一、下载openEuler镜像 1.2 打开官网&#xff0c;选择openEuler23.09 1.3 选择架构、场景以及软件包类型 初次使用的话基本上都是先安装虚拟机&#xff0c;我们大部分主机都是x86_64架构&#xff0c;场景的话就选服务器&#xff0c;软件版类型选择标准版&#xff0c;可以安装图…