字节对齐问题

发布时间 2023-12-04 14:00:05作者: __Zed

字节对齐的作用

  1. 节约内存空间,对于一个结构体 不同的成员变量顺序会影响最终存储占用的空间

  2. 加深对不同平台字节数的理解和记忆

什么是字节对齐

  1. 一般是相对于结构体而言

  2. 说人话就是,结构体最终占用的空间,往往不是看起来占用的空间

  3. 总结起来就是按照结构体的顺序挨个存,存之前必须确保当前大小是接下来要存的变量大小整数倍,最后存完要确保是结构体最大的变量大小的整数倍

  4. 可能有点抽象,主要涉及几个概念:当前大小 接下来要存的变量大小 结构体最大的变量大小

举例讲解

struct A{
uint_8 a;
uint_16 b;
uint_32 c;
uint_64 d;
}

先明确一点,系统要求的大小,学名叫系统默认对齐字节数,我目前遇到的任务,要求是4字节,因地制宜

  1. 按照顺序存储,先存a,当前大小就是0,接下来要存的变量(a)大小就是1字节(uint_8表示跨平台8位)
  2. 存b,依次是 当前大小1字节,接下来要存的b是2字节,由于:
    确保当前大小是接下来要存的变量大小整数倍
    1不是2的整数倍,所以要补充一字节到2!
  3. 存c,当前大小2+2=4字节,接下来要存的c是4字节。所以直接存就行
  4. 存d,当前大小4+4=8字节,接下来要存的d,看似8字节,实则系统要求最多4字节,所以只能分开存!!!!
  5. 结构体最大的变量大小,也就是max(1,2,4,8)得8,16是他的整数倍,所以最后不用扩
    所以:接下来要存的字节数 = min(系统要求的字节数,该变量占用的字节数)

综上所述,结构体成员变量字面上占用1+2+4+8=15字节,实际占用1+1+2+4+4+4 = 16字节!!!

其他要注意的

  1. 数组一个一个存,实际上只要数组第一个元素对齐了后面自动对齐,因为第一个前面的是n倍了,继续加同类型变量就是n+1倍
  2. 不同的顺序会导致不同的结果,比如上面这个例子,按照uint_8 uint_32 uint_16 uint_64存,存uint_32就需要对齐3个,一共需要1+3+4+2+4+4=18字节,最后在对齐整体做到4的倍数(为什么不是8呢,因为被截断了)也就是需要20字节!!
  3. 数组比如int[5],不会改变“结构体最大的变量大小”,因为是每个元素依次存储的!
  4. 全uint_8或者char(绝大多数平台都是1字节)是最好的,因为不需要任何字节对齐!

改变系统默认字节对齐数

pragma pack()命令可以指定默认对齐字节数,可选参数有1/2/4/8/16,不带参或参数非以上值时,将恢复默认值。

#pragma pack(2)
struct xxx{

}
#pragma pack()

判别步骤

注意的点

  1. 先看系统默认对齐字节数是多少,如果很大比如16,那么“接下来要存的变量大小”就是该变量实际大小,存前考虑前面的值对齐
  2. 如果系统默认字节数是1 2 这种比较小的,那么“接下来要存的变量大小”很可能要被截断!!!

步骤

  1. 按结构体内变量的顺序找,从第一个开始存,min(名义占字节,系统默认对齐字节)得到的值就是存这个变量实际小号的内存空间
  2. 依次寻找最后相加
  3. 记录最长的内存段,这个值=最大的内存片段,如果系统默认很小会被截断,比如系统默认是1,那么就是1没得商量,每个片段都是1,,最后要补到1的倍数也就是不用补