Appearance
数据对齐与大小端
考情分析
大小端和数据对齐在 408 中属于中频考点,通常以选择题出现。典型题型:给定多字节数据和起始地址,画出大端/小端的存储布局;给定结构体成员,计算对齐后的总大小和各成员偏移量。
字节序(Byte Order)
一个多字节数据(如 32 位整数 0x12345678)在内存中按字节存放时,高字节和低字节的排列顺序有两种约定。
大端(Big Endian)
高位字节存放在低地址,与人类阅读顺序一致。
| 地址 | 0x100 | 0x101 | 0x102 | 0x103 |
|---|---|---|---|---|
| 数据 | 12 | 34 | 56 | 78 |
网络协议(TCP/IP)统一使用大端,因此也叫网络字节序。
代表架构:IBM z/Series、SPARC、PowerPC。
小端(Little Endian)
低位字节存放在低地址。
| 地址 | 0x100 | 0x101 | 0x102 | 0x103 |
|---|---|---|---|---|
| 数据 | 78 | 56 | 34 | 12 |
小端的优势:取低字节时地址不随数据宽度变化,方便硬件做变宽度访问(如从同一地址读 1 字节或 4 字节,最低字节地址相同)。
代表架构:x86/x64、ARM(默认小端)。
对比与记忆
| 特性 | 大端 | 小端 |
|---|---|---|
| 低地址存放 | 最高有效字节(MSB) | 最低有效字节(LSB) |
| 人类可读性 | 自然 | 反直觉 |
| 网络传输 | 标准字节序 | 需转换 |
| 硬件取低字节 | 地址随宽度变化 | 地址固定 |
口诀:大端 = 高字节在低地址(高低),小端 = 低字节在低地址(低低)。
判断字节序(编程方法)
c
int x = 1;
if (*(char *)&x == 1) {
// 低地址存的是 1(低字节),小端
} else {
// 低地址存的是 0(高字节),大端
}数据的边界对齐
为什么对齐
CPU 按字(4 字节)或双字(8 字节)访问内存。如果一个 4 字节 int 跨越了两个对齐边界,处理器需要两次内存访问再拼接。
对齐规则
| 数据类型 | 大小 | 对齐边界 |
|---|---|---|
char | 1B | 任意地址 |
short | 2B | 2 的倍数 |
int / float | 4B | 4 的倍数 |
double | 8B | 8 的倍数 |
结构体对齐
结构体的每个成员按自身对齐要求排列,成员之间可能插入填充字节(padding)。整个结构体的总大小是最大对齐值的整数倍。
c
struct S {
char a; // 1B, 偏移 0
int b; // 4B, 对齐到 4 → 偏移 4(填充 3 字节)
short c; // 2B, 偏移 8
};
// 总大小:最大对齐 4,8+2=10 → 补齐到 12内存布局:
| 偏移 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 内容 | a | pad | pad | pad | b | b | b | b | c | c | pad | pad |
调整成员顺序可以减少填充:
c
struct S2 {
int b; // 偏移 0
short c; // 偏移 4
char a; // 偏移 6
};
// 总大小:6+1=7 → 补齐到 8节省了 4 字节,仅靠重新排列成员顺序。
未对齐访问的后果
不同架构对未对齐访问的处理不同:
| 架构 | 处理方式 |
|---|---|
| x86 | 允许但有性能惩罚(自动拆分为多次访问) |
| ARM(严格模式) | 产生对齐异常 |
| MIPS | 必须对齐,否则异常 |
例题
例 1:32 位整数 0xAABBCCDD 存储在地址 0x1000 处,分别写出大端和小端的存储内容。
大端:
| 0x1000 | 0x1001 | 0x1002 | 0x1003 |
|---|---|---|---|
AA | BB | CC | DD |
小端:
| 0x1000 | 0x1001 | 0x1002 | 0x1003 |
|---|---|---|---|
DD | CC | BB | AA |
例 2:小端机器中,short x = 0x0102 存放在地址 0x2000,从 0x2000 读一个字节得到什么?
小端存储:0x2000 放低字节 02,0x2001 放高字节 01。
读 0x2000 的一个字节,得到 0x02。
例 3:计算以下结构体的大小(假设 double 按 8 字节对齐)。
c
struct T {
char a; // 1B, 偏移 0
double b; // 8B, 对齐到 8 → 偏移 8(填充 7 字节)
int c; // 4B, 偏移 16
char d; // 1B, 偏移 20
};
// 最大对齐 8, 总大小 20+1=21 → 补齐到 24答:24 字节。若把成员重排为 double b; int c; char a; char d;,总大小仅 16 字节。
考点清单
- [ ] 大端:高字节在低地址(网络字节序)
- [ ] 小端:低字节在低地址(x86 采用)
- [ ]
字节数据对齐到 的整数倍地址 - [ ] 结构体总大小是最大对齐值的整数倍
- [ ] 成员之间可能有填充字节,调整顺序可减少浪费
- [ ] 未对齐访问:x86 允许但慢,ARM/MIPS 可能异常