数据对齐方式

熟练掌握数据对齐和大小端,可能会在选择题考察,也会在大题中间接考察。

数据对齐

数据对齐 (Data Alignment)是指数据在内存中的存放方式,它要求数据的起始地址必须是某个数(通常是 1、2、4、8)的整数倍,这个数被称为 对齐因子 (Alignment Factor)。数据对齐的目的是为了提高 内存访问 的效率,因为许多计算机系统都是按照数据的对齐边界来设计 内存访问 硬件的。

不对齐 的数据访问可能会导致 性能下降,因为处理器可能需要额外的 内存访问 来获取不完整的数据。在一些严格要求 数据对齐 的架构中,不对齐 的数据访问甚至会导致 硬件异常


在 C11 中,我们可以通过 _Alignof 来查看不同数据类型的 对齐因子

#include <stdint.h>
#include <stdio.h>

#define EVAL_PRINT(expr) printf("%-20s = %u\n", #expr, (uint8_t)(expr));

int main(void) {
    EVAL_PRINT(_Alignof(char));
    EVAL_PRINT(_Alignof(uint8_t));
    EVAL_PRINT(_Alignof(uint16_t));
    EVAL_PRINT(_Alignof(uint32_t));
    EVAL_PRINT(_Alignof(int));
    EVAL_PRINT(_Alignof(uint64_t));
    EVAL_PRINT(_Alignof(void*));
    EVAL_PRINT(_Alignof(size_t));
    return 0;
}

以上程序运行的结果如下所示,以此我们可以判断每个类型的 aglinment 大小。

$ gcc alignof.c && ./a.out
_Alignof(char)       = 1
_Alignof(uint8_t)    = 1
_Alignof(uint16_t)   = 2
_Alignof(uint32_t)   = 4
_Alignof(int)        = 4
_Alignof(uint64_t)   = 8
_Alignof(void*)      = 8
_Alignof(size_t)     = 8

以下图中的结构体定义为例,假设我们定义一个 变量,变量的类型长度为 K 个字节,那么这个 变量内存 中的地址 addr 必须是 K 的整数倍,即 addr % K == 0

a
b
b
c
c
c
c
d
struct foo {
char a;
uint16_t b;
char d;
};
int32_t c;

上图中 变量 b 和 a 之间增加了 1 个字节的 padding变量 d 的末尾也增加了 3 个字节 padding,以保证下一个 数据 的开始是 4 的整数倍。

大小端

大小端 (Endianness)是指多字节 数据在内存中的字节序,也就是 字节 的排列顺序。主要有两种存放方式:

大端序

也叫做 大端模式 (Big-Endian): 数据内部的 高位字节 存放在 低位地址低位字节 存放在 高位地址。也就是说,一个整数的第一个字节(最高有效字节)将存放在起始地址处。

小端序

也叫做 小端模式 (Little-Endian): 数据内部的 低位字节 存放在 低位地址高位字节 存放在 高位地址。也就是说,一个整数的最后一个字节(最低有效字节)将存放在起始地址处。

举一个例子,假如定义数组 long a[2] = {0x76543210, 0xFEDCBA98}long 类型的大小为 8 字节,数组a 在内存中的起始地址为 0x1000,则数组中两个元素在内存中的字节排列如下图所示:

0x10
0x32
0x54
ox76
0x00
0x00
0x00
0x00
0x00
0x00
0x00
0x00
0x76
0x54
0x32
0x10
0x98
0xBA
0xDC
0xFE
0x00
0x00
0x00
0x00
0x00
0x00
0x00
0x00
0xFE
0xDC
0xBA
0x98
0x1000
0x1000
0x1008
0x1008
Little Endian
Big Endian
注意

理解大小端的核心是要分清 字节内部的 bit 顺序多字节数据的字节顺序(大小端的核心问题)

  • 大小端(Endianness) 本身并 不涉及字节内部的位顺序,它只规定 字节之间在内存中的排列方式
  • 在绝大多数现代 CPU 架构中,一个字节(8 bit)的内部位顺序都是 bit7 在高位bit0 在低位,并且在寄存器和内存访问时,bit 顺序是一致的。

大小端 的选择通常是由计算机的 CPU 架构 决定的,不同的架构有不同的 字节序 要求。例如,Intel x86 和 x86-64 架构是 小端,而网络协议通常是 大端,因为 大端 的格式在 字节流 中的表示更加直观。

最后一句话总结一下 大小端

  • 小端序 是低位字节在低地址
  • 大端序 是高位字节在低地址