提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- 一、整数在内存中的存储
-
- [1. 二进制表示形式](#1. 二进制表示形式)
- [2. 存储方式](#2. 存储方式)
- [3. 常见整数类型](#3. 常见整数类型)
- [4. 特殊整数处理](#4. 特殊整数处理)
- [5. 内存布局示例](#5. 内存布局示例)
- 二、⼤⼩端字节序和字节序判断
-
- [1. 什么是字节序](#1. 什么是字节序)
- [2. 为什么需要关注字节序](#2. 为什么需要关注字节序)
- [3. 判断当前系统的字节序](#3. 判断当前系统的字节序)
- [4. 字节序转换函数](#4. 字节序转换函数)
- [5. 实际应用示例](#5. 实际应用示例)
- [6. 常见问题与解决方案](#6. 常见问题与解决方案)
- 三、浮点数在内存中的存储
-
- [1. IEEE 754标准](#1. IEEE 754标准)
- [2. 存储结构详解](#2. 存储结构详解)
- [3. 特殊值表示](#3. 特殊值表示)
- [4. 舍入规则](#4. 舍入规则)
- [5. 精度问题](#5. 精度问题)
- [6. 实际应用注意事项](#6. 实际应用注意事项)
- 总结
前言
这篇博客主要围绕整数、大小端字节序、浮点数在内存中的存储。
一、整数在内存中的存储
1. 二进制表示形式
整数在计算机内存中是以二进制形式存储的,使用补码表示法。补码系统有以下特点:
- 正数的补码是其二进制原码
- 负数的补码是其绝对值的二进制取反后加1
- 最高位为符号位(0表示正数,1表示负数)
例如,8位整数:
- +5 的补码:00000101
- -5 的补码:11111011(取反11111010 + 1)
2. 存储方式
整数在内存中的存储遵循以下规则:
-
大小端存储:
- 大端模式(Big-endian):高位字节存储在低地址
- 小端模式(Little-endian):低位字节存储在低地址
- 例如0x12345678在内存中的存储:
- 大端:12 34 56 78
- 小端:78 56 34 12
-
对齐方式:
- 现代CPU通常要求数据按照其字长对齐
- 32位系统通常要求4字节对齐
- 64位系统通常要求8字节对齐
3. 常见整数类型
不同编程语言中的整数类型及其典型存储大小:
| 类型 | C/C++ | Java | Python | 存储大小 |
|---|---|---|---|---|
| 有符号8位 | char | byte | int | 1字节 |
| 无符号8位 | unsigned char | - | - | 1字节 |
| 有符号16位 | short | short | int | 2字节 |
| 无符号16位 | unsigned short | char | - | 2字节 |
| 有符号32位 | int | int | int | 4字节 |
| 无符号32位 | unsigned int | - | - | 4字节 |
| 有符号64位 | long long | long | int | 8字节 |
| 无符号64位 | unsigned long long | - | - | 8字节 |
注意:Python的int类型会自动扩展以适应大整数,实际存储大小会动态变化。
4. 特殊整数处理
-
零的表示:
- 所有位都为0
- 补码系统中+0和-0的表示相同
-
最小负数的表示:
- 对于n位整数,最小负数是-2^(n-1)
- 例如8位整数的最小值是-128(补码:10000000)
-
溢出处理:
- 无符号整数:回绕(0xFFFFFFFF + 1 = 0x00000000)
- 有符号整数:行为未定义(C/C++)或抛出异常(某些语言)
5. 内存布局示例
32位整数0xAABBCCDD在小端系统中的内存布局:
地址 内容
0x1000 DD
0x1001 CC
0x1002 BB
0x1003 AA
理解整数在内存中的存储方式对于以下场景很重要:
- 网络协议开发(处理字节序)
- 二进制文件解析
- 内存敏感型应用开发
- 跨平台数据交换
二、⼤⼩端字节序和字节序判断
1. 什么是字节序
字节序(Endianness)指的是多字节数据在计算机内存中的存储顺序。主要有两种类型:
-
大端字节序(Big-Endian):
- 最高有效字节(MSB)存储在最低的内存地址
- 类似于我们书写数字的顺序(从左到右)
- 示例:0x12345678 在内存中的存储顺序为 12 34 56 78
- 采用大端序的架构:PowerPC、SPARC、早期的ARM等
-
小端字节序(Little-Endian):
- 最低有效字节(LSB)存储在最低的内存地址
- 类似于"倒序"存储
- 示例:0x12345678 在内存中的存储顺序为 78 56 34 12
- 采用小端序的架构:x86、x86-64、现代ARM(可配置)
2. 为什么需要关注字节序
字节序问题主要在以下场景中需要注意:
-
跨平台数据传输:
- 网络通信(网络协议通常采用大端序)
- 文件格式交换
- 不同架构设备间的数据共享
-
二进制数据处理:
- 解析网络数据包
- 读取二进制文件
- 处理硬件寄存器
-
调试和逆向工程:
- 分析内存数据时需要知道字节序
- 理解寄存器和内存中的值对应关系
3. 判断当前系统的字节序
方法1:使用C程序判断
c
#include <stdio.h>
int main() {
unsigned int num = 0x12345678;
unsigned char *p = (unsigned char *)#
if (*p == 0x12) {
printf("Big-Endian\n");
} else if (*p == 0x78) {
printf("Little-Endian\n");
} else {
printf("Unknown Endianness\n");
}
return 0;
}
方法2:使用Python判断
python
import sys
if sys.byteorder == 'little':
print("Little-Endian")
else:
print("Big-Endian")
方法3:使用命令行工具(Linux)
bash
lscpu | grep "Endian"
# 或
echo -n I | od -to2 | head -n1 | cut -f2 -d" " | cut -c6
# 输出1表示小端,0表示大端
4. 字节序转换函数
C语言中的转换函数
c
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong); // 主机到网络(32位)
uint16_t htons(uint16_t hostshort); // 主机到网络(16位)
uint32_t ntohl(uint32_t netlong); // 网络到主机(32位)
uint16_t ntohs(uint16_t netshort); // 网络到主机(16位)
Python中的转换
python
import socket
value = 0x12345678
network_order = socket.htonl(value) # 主机序转网络序
host_order = socket.ntohl(network_order) # 网络序转主机序
手动实现字节序转换
32位整数的转换示例:
c
uint32_t swap_endian(uint32_t val) {
return ((val << 24) & 0xff000000) |
((val << 8) & 0x00ff0000) |
((val >> 8) & 0x0000ff00) |
((val >> 24) & 0x000000ff);
}
5. 实际应用示例
网络协议处理
处理TCP/IP首部时需要注意:
- IP首部中的所有字段都是大端序
- TCP首部中的所有字段都是大端序
- 接收数据后需要使用ntohs/ntohl转换
文件格式处理
常见二进制文件的字节序:
- PNG:大端序
- GIF:小端序
- JPEG:大端序
- ELF:与目标平台相关
嵌入式开发
访问硬件寄存器时需要特别注意:
- 读取传感器数据
- 配置设备寄存器
- 与FPGA通信
6. 常见问题与解决方案
-
数据错位问题:
- 现象:接收到的数据与预期不符
- 解决方案:检查发送方和接收方的字节序是否一致
-
跨平台兼容性:
- 使用标准网络字节序(大端)进行数据交换
- 在文件头中加入字节序标识
-
性能考虑:
- 在已知字节序的系统中避免不必要的转换
- 批量转换优于逐个转换
-
调试技巧:
- 使用十六进制查看器检查原始数据
- 打印内存内容对比预期值
三、浮点数在内存中的存储
1. IEEE 754标准
浮点数在内存中的存储遵循IEEE 754标准,该标准定义了浮点数的二进制表示格式。主要有两种格式:
-
单精度浮点数(32位):
- 1位符号位
- 8位指数位
- 23位尾数位
-
双精度浮点数(64位):
- 1位符号位
- 11位指数位
- 52位尾数位
2. 存储结构详解
符号位(Sign)
- 位于最高位
- 0表示正数,1表示负数
- 示例:-3.14的符号位为1
指数部分(Exponent)
- 采用偏移表示法(Bias)
- 单精度:偏移量为127
- 双精度:偏移量为1023
- 实际指数 = 存储值 - 偏移量
- 示例:单精度浮点数中存储的指数值为130,实际指数为130-127=3
尾数部分(Mantissa/Significand)
- 采用隐含最高位1的表示方法(规范化数)
- 只存储小数部分
- 示例:1.1011只需存储1011
3. 特殊值表示
IEEE 754标准还定义了特殊值的表示:
- 零值:全0表示
- 无穷大:指数全1,尾数全0
- +∞:符号位0
- -∞:符号位1
- NaN(非数):指数全1,尾数非0
- 用于表示无效运算结果(如0/0)
4. 舍入规则
IEEE 754定义了四种舍入模式:
- 向最近值舍入(默认)
- 向零舍入
- 向正无穷舍入
- 向负无穷舍入
5. 精度问题
由于浮点数的二进制表示特性,会导致一些十进制小数无法精确表示,如:
- 0.1在二进制中是无限循环小数
- 这解释了为什么0.1 + 0.2 != 0.3
6. 实际应用注意事项
在编程中需要注意:
-
避免直接比较浮点数相等
- 应该比较两者差值是否小于某个极小值(如1e-6)
-
注意累积误差
- 多次运算可能导致误差累积
-
选择合适的浮点类型
- 根据精度需求选择单精度或双精度
-
注意特殊值的处理
- 检查NaN和无穷大的情况
总结
以上就是本文总结的内容,关于数据在内存中的存储。