当前位置: 首页 > news >正文

数据的存储

标题

目录

数据存储

整数在内存中的存储

存储规则

机器码

大小端字节序以及其判断

浮点数在内存中的存储


数据存储

整数在内存中的存储

存储规则

计算机内部使用二进制编码表示整数,带符号的整数称为机器数(包含符号位和数值位),实际数值是真值。

符号位数值位
0为正,1为负数的绝对值

机器码

整数的二进制表示方法有三种,即原码、反码、补码。

正整数的原码、反码、补码相同。

负整数:

  • 原码为将数值按照正负数的形式翻译为二进制形式得到的数值码。
  • 反码为原码的符号位不变,数值位按位取反。
  • 补码为反码加一。

在计算机中,统一采用补码的存储及运算。

计算机硬件中只有加法器,补码可以将符号位和数值域统一处理,减法运算转换为加法运算,简化了运算器的设计,不需要额外的硬件电路。

大小端字节序以及其判断

概念

如下图所示,在vs调试当中,我们取地址a,可以发现它是按照字节为单位倒着存储的。

那下面我们一起来探讨一下数据在内存中存储顺序的问题。

在计算机中,内存的编制是由字节组成的,一个多字节的数据会占用连续多个字节。

按照不同的存储顺序,我们分为大端模式小端模式

大端存储
数据的低位字节内容保存在内存的⾼地址处,⽽数据的⾼位字节内容,保存
在内存的低地址处。
小端存储
数据的低位字节内容保存在内存的低地址处,⽽数据的⾼位字节内容,保存
在内存的⾼地址处。
  • 大端字节序常用于:网络协议、嵌入式、单片机。
  • 小端字节序常用于:x86 架构 PC、Windows、Linux(日常电脑都是小端)。

练习1

利用大小端字节序的概念,设计一个简单的程序判断当前机器的字节序。

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

代码解析:

  • 定义 int i=1,整形为4个字节,在二进制编码当中为
00 00 00 01

高字节为: 00 00 00

低字节为: 01

  • 取变量 i 的低地址,将其强制转换为char类型,只读取1个字节,并解引用,取出第一个字节的值。

主函数判断

内存低地址 → 高地址 01 00 00 00

返回值为1,为小端。

高字节 → 低地址 00 00 00 01

返回值为0,为大端。

练习2

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

运行结果如下:

代码解析:

由于打印格式为%d需要整形提升

提升规则:

  1. 有符号数:按符号位扩展(高位补符号位)
  2. 无符号数:高位补0
  • a的原码: 10000000 00000000 00000000 00000001

反码: 111111111 111111111 111111111 111111110

补码: 111111111 111111111 111111111111111111

内存补码为11111111

提升后补码为 111111111 111111111 111111111 111111111 ,真值为-1。

  • 同理,b打印也为-1。
  • 同理,c的内存存储为11111111

提升后补码为00000000 00000000 00000000 111111111,真值为255。

练习3

int main() { char a = -128; printf("%u\n", a); return 0; }
  • %u为无符号数打印形式
  • a为char类型需要整型提升
-128 原码 -> 10000000 00000000 00000000 10000000 反码 -> 11111111 11111111 11111111 01111111 补码 -> 11111111 11111111 11111111 10000000 内存存储 -> 10000000 整型提升 -> 11111111 11111111 11111111 10000000

结果如下图所示:

练习4

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

结果如下图所示:

练习5

#include <string.h> int main() { char a[1000]; int i; for ( i = 0; i < 1000; i++) { a[i] = -1 - i; } printf("%zd", strlen(a)); return 0; }
  • 求数组a的字符串长度-> 在‘\0’(0)前的字符串长度
  • -1-i为依次-1的循环模式,范围为-1 -> -128 -> 127-> 0

结果如下图所示:

练习6

unsigned char i = 0; int main() { for(i = 0; i <=255; i++) { printf("hello world\n"); } return 0; }
  • 代码结果为无限循环。
  • 无符号数的取值范围为0~255。
  • 如果数值大于255(二进制为11111111),增一为100000000,取八位为00000000,即返回0。

练习7

#include <Windows.h> int main() { unsigned int i; for (i = 9; i >= 0; i--) { printf("%u\n",i); Sleep(1000); } return 0; }
  • 代码结果为无限循环。
  • 无符号整数的取值范围很大,为0-32位的1组成的数。
  • 所以 i>=0 的判断恒成立,程序会无限循环。

结果如下图所示:

练习8

int main() { int a[4] = { 1,2,3,4 }; int* ptr1 = (int*)(&a + 1); int* ptr2 = (int*)((int)a + 1); printf("%x,%x\n", ptr1[-1], *ptr2); return 0; }

如图所示:

地址偏移(字节): 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ... 对应元素: ───── a[0]=1 ────┼───── a[1]=2 ────┼───── a[2]=3 ────┼───── a[3]=4 ────┼ 数组外未知内存 字节归属: 0~3号字节 4~7号字节 8~11号字节 12~15号字节 16号及以后
ptr1: a 表示数组首元素的地址 &a 表示整个数组的地址 &a+1 表示跳过整个数组 (int*)(&a+1)即ptr1 解引用表示一个数组的单个元素 ptr1[-1] 即为4
偏移: 0 ... 15 16 ↑ ↑ &a ptr1 (&a+1 的位置) 偏移: 12 13 14 15 | 16 ↑ ↑ a[3]=4 ptr1 (ptr1 - 1)
ptr2: (int)a 表示将a强制转换为普通整数 (int)a+1 纯数值 +1,只偏移 1 个字节 再强制转回 int*,得到 ptr2
偏移: 0 1 2 3 ↑ ↑ a ptr2 a[0]首字节 a[0]第2字节

结果如下图所示:

浮点数在内存中的存储

基础划分

类型总位数符号位 (S)阶码 (E)尾数 (M)
float(单精度)32 bit1 bit8 bit23 bit
double(双精度)64 bit1 bit11 bit52 bit

内存布局(高位 → 低位):符号位 S + 阶码 E + 尾数 M

存储规则

浮点数在计算机内部的表示方法:

  • S表示符号位,0为正数,1为负数。
  • M表示有效数字,区间为1<=M<2。
  • E为指数位。

存储过程

浮点数存放
有效数字M
  • 约定:二进制浮点数整数部分固定为 1,所以可以被舍去,只存小数部分,写作1.M。
  • 这一步叫隐藏整数 1,可以节省 1 位空间。
指数E
  • 规定:E是一个无符号整数。但在科学计数法中,E可以有负数的形式出现。
  • 所以当存入内存时,指数E需要将原有的真实值加上一个中间数。
  • 对于8位的E,取值范围为0-255,中间数位127。
  • 对于11位的E,取值范围为0-2047,中间数位1023。
浮点数取值
规格化浮点数
  • E不为全0或不为全1
指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第⼀位的1。
非规格化浮点数
  • E为全0

将隐藏的整数 1 取消,小数变为 0.M。表⽰±0,以及接近于0的很小的数字。

  • E为全1
如果有效数字M全为0,表⽰±无穷⼤(正负取决于符号位s)。
http://www.cnnetsun.cn/news/4041436.html

相关文章:

  • 免费开源的桌面围栏工具NoFences:3分钟让图标各归其位
  • 微信聊天记录存哪里才安心?WeChatMsg 本地留档实操手册
  • 面试官:Workflow 和 Agent 有什么区别?如何选型?
  • 一篇搞定 OneNote 笔记迁移:onenote-md-exporter 完整使用指南
  • AIGC无限画布前端实现:从Canvas架构到性能优化全解析
  • QQ空间备份三步走:用QQ空间导出助手免费永久保存十年青春
  • OpenClaw:AI Agent如何重塑测试自动化,从脚本执行到智能决策
  • 利用GPT-5.6模拟国自然评审的评分思路:把淘汰项改成加分项
  • 三分钟装好开源番茄钟:把macOS菜单栏变成你的专注控制台
  • 02.01.02.泛微OA Ecology10 (开发环境搭建idea安装配置)
  • Dell XPS 13 (2026)评测:预算Windows笔记本新标杆,能否击败MacBook Neo?
  • 暗黑2角色存档修改完整教程:用Diablo Edit2免费修改角色、装备与任务进度
  • 米哈游扫码登录器上手指南:如何用 3 分钟搭好你的自动抢码工具
  • 满足各类环境测试需求大阵列芯片测试座-测试良率高
  • OpenProject容器化部署完整实操:排掉三个高频坑,半小时把项目管理平台跑起来
  • Agent 能不能上线,关键看评估能不能真正控制业务流程
  • Knowledge Graph Augmented Large Language Models for Disease Prediction
  • AgentScope 2.0:专为托管AI智能体打造的企业级云原生平台
  • ColabFold 批量处理实战:一次跑完几百条序列的蛋白质结构预测完整流程
  • 微信公众号数据采集完整指南:3个实战场景玩转搜狗微信搜索爬虫
  • JPEXS Free Flash Decompiler 实战指南:一条命令跑通 SWF 反编译、修复与资源提取全流程
  • ARM架构KVM虚拟化支持现状分析
  • 单片机常用型号参考
  • 137、顶会注意力机制复现(二):PKINet上下文先验注意力适配YOLOv12——ICCV2023核心思想解析与Area Attention替换实验涨点对比
  • 189、LLC谐振变换器的样机调试实战(可靠性测试)
  • AI时代开发者如何避免“结论泛滥”:从代码搬运到系统思维的实践指南
  • langgraph笔记(2) fastapi笔记
  • 微信聊天记录导出完整指南:从本地备份到年度报告一次搞定
  • Win11玩不动老游戏?DDrawCompat:让DirectDraw老游戏起死回生的开源兼容层
  • 零代码开源自动化工具上手:宏录制把每天1小时的重复劳动缩短到10分钟