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,一经查实,立即删除!

相关文章

贪心,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,选装完再加上。

好用的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;去掉那些已经看过的内容…

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…

OpenEuler 的安装过程记录

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

【稳定检索/投稿优惠】2024年环境、资源与区域经济发展国际会议(ERRED 2024)

2024 International Conference on Environment, Resources and Regional Economic Development 2024年环境、资源与区域经济发展国际会议 【会议信息】 会议简称&#xff1a;ERRED 2024 大会地点&#xff1a;中国杭州 会议官网&#xff1a;www.icerred.com 会议邮箱&#xff1…

基本元器件 - 二极管

目录 二极管的主要参数 二极管的分类 整流二极管 快恢复二极管&#xff08;FRD&#xff09; 稳压&#xff08;齐纳&#xff09;二级管 瞬态电压抑制器&#xff08;TVS&#xff09; 开关二极管 肖特基二极管&#xff08;SBD&#xff09; 正偏与反偏 常用封装 伏安特性…

第二篇 多路数据选择器

实验二 多路数据选择器 2.1 实验目的 理解多路数据选择器的概念&#xff1b; 使用门级结构描述实现多路选择器&#xff1b; 使用行为描述实现多路选择器&#xff1b; 完成实验设计、仿真&#xff0c;并在DE1-SOC上验证电路。 2.2 原理介绍 在多路数据传送过程中&#xf…

238.除以自身以外数组的乘积

给你一个整数数组 nums&#xff0c;返回数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积。 题目数据保证数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#xff0c;且在 O(n) 时间复杂度内完…

Mac连接U盘后怎么读取 Mac连接U盘后怎么取消只读模式

在使用Mac电脑时&#xff0c;连接U盘是一项常见的需求。无论是传输文件还是备份数据&#xff0c;正确地读取U盘对于Mac用户至关重要。然而&#xff0c;当连接的U盘格式为NTFS时&#xff0c;在Mac中进行数据编辑会遇到一些困难。下面我们来看看Mac连接U盘后怎么读取&#xff0c;…

进程线程(一.2)

进程与线程&#xff08;一&#xff09; 并发编程并发与并行高并发 进程特征什么是进程&#xff1f;线程&#xff1f;进程与程序的区别进程与线程区别进程的五状态进程的种类 查看进程命令ps auxps axjpstreekill 进程的创建fork函数fork总结vfork函数fork与vfork区别 获取进程I…