C语言:数据在内存中的存储

C语言:数据在内存中的存储

    • 整数存储
      • 原码、反码、补码
        • 转换规则
        • 数据与内存的关系
    • 大小端字节序
    • 浮点数存储
      • IEEE 754标准
      • 存储过程
      • 取用过程
    • 数据的存储范围


整数存储

原码、反码、补码

整数的2进制表示方法有三种,即原码、反码和补码
三种表示方法均有符号位和数值位两部分,符号位用0表示“正”,用1表示“负”

有符号整数最高位的一位是被当做符号位,剩余的都是数值位。
无符号整数所有的位都是数值位


转换规则

正整数的原、反、补码都相同。
负整数的三种表示方法各不相同。

原码:直接将数值按照正负数的形式翻译成二进制得到的就是原码。
反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。
补码:反码+1就得到补码。

正数:
在这里插入图片描述
对于int(整形),计算机会给内存开辟4个字节即32个比特来存放a。由于在此a是正数,第一位符号位为0,数值为5,转化为二进制就是101,存在最后。正数的原反补三码相同。

负数:
在这里插入图片描述
由于在此a是负数,在原码中,第一位是符号位,存放1。
反码:符号位不变,保持为1。其余位按位取反,即0变1,1变0.
补码:在反码的情况下加1。
而补码想要变回原码,也是相同的步骤,即先取反后加一。

数据与内存的关系

首先,我们在内存中存储的数据是以补码的形式存储的。我们用代码定义a,b为5和-5,然后观察其在内存中的值:
在这里插入图片描述
由于二进制实在难于分辨,所以编译器在向程序员呈现计算机存储的值的时候,会转为16进制。我们从上图中可见,a的存储是可以理解的,可b的值却不是-00000005。这个fffffffb其实就是-5的补码的16进制形式,由此可以证明,内存存储数据就是以补码的形式。
那为什么内存要存补码?

  • 可以把符号与数值统一处理,把数字的正负放在码值中,不用额外区分。
  • 可以使加法减法统一处理(CPU只有加法计算器)。

第一点其实是容易理解的,那为什么用补码可以统一加减法呢?我们以下面的代码为例:
在这里插入图片描述
以上代码中,计算机想要完成3 - 5,于是CPU将其转化为了3 + (-5),然后直接将补码相加,然后转回原码,得到的就是正确答案。

可见,虽然代码是减法,但是在计算机处理的时候,只做了加法运算。这样可以减少计算机硬件的消耗,只需要在CPU内部做好加法的硬件即可。


大小端字节序

讲完整数的存储后,我提出一个新的问题:int类型占用四个字节,请问这四个字节在内存中是从高到低,还是从低到高存储?这就涉及到了大小端字节序的问题。

int a = 0x11223344;

变量a是十六进制的11223344,十六进制转二进制,每一位数字转为四位二进制,所以每两个十六进制数字占一个字节。上述代码中11223344各占一个字节。当一个变量占用多个字节,字节会区分高位与低位:

11为高位字节序
44为低位字节序

而在内存中一个变量的多个字节有两种存储顺序:

大端字节序:将一个数值的低位字节序存储到内存的高地址处
在这里插入图片描述

小端字节序:将一个数值的低位字节序存储到内存的低地址处
在这里插入图片描述

值得注意的是:大小端字节序并不取决于编译器,而取决于计算机的硬件实现

接下来我们设计一个程序来检测我们的计算机是大端字节序还是小端字节序:
大小端存储,是发生在一个变量同时占用多个字节的情况下的,想要检测计算机的大小端,那就需要在一个变量内部区分出高地址与低地址
而指针就有这个特性:指向某个变量的指针,其地址为该变量所有地址中最低的那个地址,这样我们把一个指向占用多个字节变量的指针强制转化为char*指针,就可以访问到其最低位的地址了

代码如下:

int a = 1;
char* pa = (char*)&a;if (*pa == 0)printf("大端字节序\n");
else if (*pa == 1)printf("小端字节序\n");

以上代码中,我们定义了一个a = 1,那么a的十六进制就是0x 00 00 00 01,其低位字节序为01,高位字节序为00。我们利用char*指针取到低地址处的变量后,如果低地址为00,说明高位存储在了低地址,是大端字节序;如果低地址为01,说明低位存储在了低地址,是小端字节序。


浮点数存储

IEEE 754标准

在C语言中,浮点数的存储是基于IEEE 754标准来实现的。
IEEE 754规定:任何一个二进制浮点数V,都可以存储为以下形式:

V = (-1)s × M × 2E

  • (-1)s:表示符号位,当S = 0, V为正数;S = 1, V为负数
  • M:表示有效数字,1 <= M < 2
  • 2E:表示指数位

接下来我详细讲解一下这套规则:
S:这个很好理解,即用于控制浮点数的正负

M
一个二进制的浮点数,其一定由0与1构成,比如1011.0101这个浮点数,为了统一处理,我们==将所有浮点数的最左位1放在小数点左边,其余位放在小数点右边。

以下是一些示例:

1011.0101 -> 1.0110101
0.00001011 -> 1.011
1111111.11111 -> 1.11111111111
0.0001 -> 1.0

在这种转化下,M一定是1.xxxxx,所以有1 <= M < 2

E
经过上述转化,那就会发生小数点的偏移,为了矫正这个偏移量,于是存在了E。
比如1011.0101 -> 1.0110101这个过程,其小数点偏移了三位,那就有1011.0101 = 1.0110101 * 2 ^ 3,此时E就表示2的指数,E = 3

再比如0.00001011 -> 1.0110.00001011 = 1.011 * 2 ^ (-5),此时E = -5

理解了这套规则后,我们尝试转化一个数字:-5.0

-5.0,为负数,所以S = 1
5.0转化为二进制为:101.0
小数点左移两位:101.0 -> 1.01,故M = 1.01E = 2
综上:-5.0 = -101.0 = (-1)1 × 1.01 × 22


存储过程

知道了浮点数的存储规则后,我们再看看这个数据是如何存放在内存中的。
想要存储一个浮点数,经过上述转换规则,也就是要存储SME三个数据

对于单精度浮点数float,第一位分配给S,为E分配了8个bit位,M分配了23个bit位:
在这里插入图片描述
对于双精度浮点数double,第一位分配给S,为E分配了11个bit位,M分配了52个bit位:
在这里插入图片描述
S

直接判断浮点数正负,然后在第一位存入0 / 1 即可

M

先前我们强调过,1.0 <= M < 2,即M一定是1.xxx的形式,所以小数点前第一位一定是1,所以可以省略掉M的第一位1,只存储小数点后面的数字。比如保存 1.01 只存储 01,小数点前的1被省略了

E

对于float而言,其分配了8位bit存储E,所以E的存储范围是[0, 255]。但是科学计数法中,指数可以为负数,所以不能从0开始存储,于是给出一个中间数,用于调节正负。存入之前,先加上一个中间数保证所有的E都被转化为正数,当取出E使用的时候,再减去中间数
float的中间数是127,比如我们的E = -9,那么存储进内存的时候实际存储的是-9 + 127 = 118;如果E = 20,存储进内存的时候实际存储的是20 + 127 = 147。所以E的实际存储范围是:[-127, 128]
double同理,其中间数是1023,存入数据前要先加上这个中间数


取用过程

将浮点数从内存中取出来,其实就是以上过程的逆过程:

  1. 先取出SME
  • M时,取到的是小数点后的数据,要再在小数点前面加上1
    比如从内存中取到的数据为:101101,那么M = 1.101101
  • E时,要减掉中间数
    比如取到的数据为130,那么E = 130 -127 = 3
  1. 根据 V = (-1)s × M × 2E 计算得到浮点数

以上是一般情况的取用,但是还有两种特殊情况:

  • E为全0:

表⽰±0,以及接近于0的很⼩的数字。

  • E为全1:

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


数据的存储范围

此处以signed char为例,intlong等以此类推即可。
我们首先列举一下signed char类型可能存在的码值:

0000 0000(0)
0000 0001(1)
......
0111 1111(127)
1000 0000(?)
1000 0001(-127)
......
1111 1110(-2)
1111 1111(-1)

以上列举中,以4bit为一单位,共8bit,即1字节。根据原码与补码的转化,以上规则应该十分清晰,唯一的问题就是1000 0000是什么?
先根据一般的转化规则:

1000 0000以1开头,说明这是一个负数,转原码需要取反 +1
取反:1111 1111
+1:1 0000 0000,可以发现此时发生了进位,截断为8位就是0000 0000,也就是0
可是我们的0已经有0000 0000来表示了,1000 0000再表示0就显得多余了
于是规定:1000 0000用于表示-128
这也是符合一定逻辑的,因为你会发现1000 0000 + 1 = 1000 0001,也就是1000 0000 + (-1) = -127,那么1000 0000表示为-128也就是合理的了

在这里插入图片描述
以上图片中,顺时针走下去,下一个数字就是上一个数字 +1,比如1 = 0 + 1-1 = -2 +1

在边界处有两个特例:

-1 + 1 = 0这个计算符合数学逻辑,但不是直接计算得到的,因为-1的补码为1111 1111,0的补码为0000 0000-1 + 1 = 1111 1111 + 1 = 1 0000 0000 ,由于发生了进位,此时有9位数据,要发生一次截断,导致1 0000 0000变成了0000 0000,所以最后得到了0。

127 + 1 = -128这是一个特例,因为我们规定了1000 0000-128,所以此处会发生这个不符合数学逻辑的情况。

总结:

  • signed char的存储范围是:[-128, 127]
  • signed char发生了这个范围以外的计算,要注意超过127的数值,会从-128开始重新计算,因为127 + 1 = -128

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

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

相关文章

【Linux】进程间通信之共享内存

文章目录 引入共享内存的原理共享内存的相关接口shmget()shmat()shmdt()shmctl() 共享内存的简单使用共享内存的特点 引入 进程间通信&#xff0c;顾名思义就是一个进程和另一个进程之间进行对话&#xff0c;以此完成数据传输、资源共享、通知事件或进程控制等。 众所周知&am…

Nodejs基于vue的个性化服装衣服穿搭搭配系统sprinboot+django+php

本个性化服装搭配系统主要根据用户数据信息&#xff0c;推荐一些适合的搭配穿搭&#xff0c;同时&#xff0c;用户也可自己扫描上传自身衣物以及输入存放位置&#xff0c;搭配后存储到“我的搭配”中&#xff0c;以便下次挑选&#xff0c;既可以节省搭配时间&#xff0c;也方便…

Stable Video Diffusion(SVD)视频生成模型发布 1.1版

前言 近日&#xff0c;随着人工智能技术的飞速发展&#xff0c;图像到视频生成技术也迎来了新的突破。特别是Stable Video Diffusion&#xff08;SVD&#xff09;模型的最新版本1.1&#xff0c;它为我们带来了从静态图像生成动态视频的全新能力。本文将深入解析SVD 1.1版本的核…

Vue3使用JSX/TSX

文章目录 1. 什么是 JSX & TSX?JSX&#xff08;JavaScript XML&#xff09;TSX&#xff08;TypeScript XML&#xff09; 2.Vue3 中使用 TSX基本渲染 & 响应式 & 事件 3.JSX 和 template 哪个好呢&#xff1f;总结 1. 什么是 JSX & TSX? 提示&#xff1a;JSX…

【K8s】初识PV和PVC

​ 目录 收起 O、致谢 一、前言 二、Volume 2.1 什么是Volume 2.2 为什么要引入Volume 2.3 Volume类型有哪些 2.4 Volume如何使用 2.4.1 通过emptyDir共享数据 2.4.2 使用HostPath挂载宿主机文件 2.4.3 挂载NFS至容器 三、PV和PVC 3.1 什么是PV和PVC 3.2 为什么要引入PV和PVC 3…

【QT+QGIS跨平台编译】之五十九:【QGIS_CORE跨平台编译】—【错误处理:字符串错误】

文章目录 一、字符串错误二、处理方法三、涉及到的文件四、宽字节与多字节问题五、字符转换处理一、字符串错误 常量中有换行符错误: 也有const char * 到 LPCWSTR 转换的错误 二、处理方法 需要把对应的文档用记事本打开,另存为 “带有BOM的UTF-8” 三、涉及到的文件 src…

J17资本合伙人SKY LAI确认出席Hack .Summit() 2024区块链开发者盛会

J17资本合伙人SKY LAI确认出席由 Hack VC 主办&#xff0c;并由 AltLayer 和 Berachain 联合主办&#xff0c;与 SNZ 和数码港合作&#xff0c;由 Techub News 承办的Hack.Summit() 2024区块链开发者盛会。 J17资本合伙人SKY LAI负责管理公司的Web3基金&#xff0c;投资领域涵盖…

vivo 在离线混部探索与实践

作者&#xff1a;来自 vivo 互联网服务器团队 本文根据甘青、黄荣杰老师在“2023 vivo开发者大会"现场演讲内容整理而成。 伴随 vivo 互联网业务的高速发展&#xff0c;数据中心的规模不断扩大&#xff0c;成本问题日益突出。在离线混部技术可以在保证服务质量的同时&…

第104讲:数据库分库分表的意义与实现策略(MyCat)

文章目录 1.分库分表的目的2.分库分表的拆分策略2.1.垂直拆分2.2.水平拆分 3.Mycat水平拆分的分片规则 1.分库分表的目的 互联网中的应用程序&#xff0c;随着公司的发展&#xff0c;应用系统的使用人数、数据量都再持续增长&#xff0c;数据库层面就会产生一定的瓶颈。 如果…

Transformer之Residuals Decoder

The Residuals 我们需要提到的编码器架构中的一个细节是&#xff0c;每个编码器中的每个子层(self-attention,&#xff0c;ffnn)周围都有一个残余连接&#xff0c;然后是 layer-normalization 步骤。 如果我们要可视化向量和与 self attention 相关的 layer-norm 运算&#x…

基于视觉识别的自动采摘机器人设计与实现

一、前言 1.1 项目介绍 【1】项目功能介绍 随着科技的进步和农业现代化的发展&#xff0c;农业生产效率与质量的提升成为重要的研究对象。其中&#xff0c;果蔬采摘环节在很大程度上影响着整个产业链的效益。传统的手工采摘方式不仅劳动强度大、效率低下&#xff0c;而且在劳…

图像处理基础——频域、时域

傅里叶分析不仅仅是一个数学工具&#xff0c;更是一种可以彻底颠覆一个人以前世界观的思维模式。 一、什么是频域 时域 时域是信号在时间轴随时间变化的总体概括&#xff1b;频域是把时域波形的表达式做傅立叶等变化得到复频域的表达式&#xff0c;所画出的波形就是频谱图&a…

Docker技术概论(8):Docker Desktop原生图形化管理

Docker技术概论&#xff08;8&#xff09; Docker 原生图形化管理 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this article:…

字节序转换函数

目录 为什么要字节序转换&#xff1f;网络协议指定通讯字节序为大端字节序转换函数主机字节序转换为网络字节序hton1 (host to network l是length指长整型)htons (host to network s是short指短整型)两个函数的代码案例 网络字节序转主机字节序ntoh1ntohs 为什么要字节序转换&a…

【报名指南】2024年第九届数维杯数学建模挑战赛报名全流程图解

1.官方报名链接&#xff1a; 2024年第九届数维杯大学生数学建模挑战赛http://www.nmmcm.org.cn/match_detail/32 2.报名流程&#xff08;电脑与手机报名操作流程一致&#xff09; 参赛对象为在校专科生、本科生、研究生&#xff0c;每组参赛人数为1-3人&#xff08;指导老师不…

【MySQL】数据查询——DQL基本数据库查询

目录 查询语法1. 查询表中所有的数据行和列&#xff0c;采用“*”符号2. 查询表中指定列的数据。3. 在查询中使用别名&#xff0c;使用“AS”关键字。4. 在查询中使用常量列&#xff1a;如果需要将一些常量的默认信息添加到输出结果中&#xff0c;以方便统计或计算。可以使用常…

Unity 预制体与变体

预制体作用&#xff1a; 更改预制体&#xff0c;则更改全部的以预制体复制出的模型。 生成预制体&#xff1a; 当你建立好了一个模型&#xff0c;从层级拖动到项目中即可生成预制体。 预制体复制模型&#xff1a; 将项目中的预制体拖动到层级中即可复制。或者选择物体复制粘贴。…

Jenkins自动化部署之流水线模式部署

文章目录 任务类型Pipeline流水线项目声明式的Pipeline脚本式Pipeline 示例脚本生成Tools配置示例 高级Pipeline Script from SCM 任务类型 在Jenkins中&#xff0c;有不同类型的任务&#xff08;项目&#xff09;适用于不同的构建需求。以下是一些常见的Jenkins任务类型&…

MYSQL03高级_新增用户、授予权限、授权底层表结构、角色理解

文章目录 ①. 登录服务器操作②. 用户的增删改③. 修改用户密码④. MySQL8密码管理⑤. 权限列表及原则⑥. 授予查看回收权限⑦. 底层权限表操作⑧. 角色的理解 ①. 登录服务器操作 ①. 启动MySQL服务后,可以通过mysql命令来登录MySQL服务器,命令如下: mysql –h hostname|hos…

32单片机基础:TIM输出比较

这个输出比较功能是非常重要的&#xff0c;它主要是用来输出PWM波形,PWM波形又是驱动电机的必要条件&#xff0c;所以你如果想用STM32做一些有电机的项目&#xff0c;比如智能车&#xff0c;机器人等。 IC: Input Capture 输入捕获 CC:Capture/Compare一般表示输入捕获和输出…