结构体中的字节对齐和紧凑打包

date
Nov 20, 2024
slug
struct-byte-align-packed
status
Published
tags
c/c++
summary
type
Post
网上很多字节对齐的介绍,这里就不写了,简单来说就是通过多补充字节来提高系统读取内存时的效率,编译器一般会对结构体进行对齐,32位程序是4字节,64位系统是8字节,以下的一些例子都使用Clang 14.0编译64位程序。
 
比如这样的结构体
所有元素的总大小是15字节,但是sizeof的输出24字节。原因是在8字节对齐的情况下,变量a之后会补充1个字节和b对齐,然后b之后再补充4个字节,让a+b总大小达到8字节,这样和变量c对齐了,变量d由于只有4个字节,也需要再补充4个字节变成8字节,这样最后的实际总大小就是1+1+2+4+8+4+4=24字节。打印b和c的结构体偏移量
offset b:2 offset c:8
b的偏移量2,c的偏移量是8。可以看到编译器帮我们完成了字节对齐,但是结构体的大小增大了一大半,实际上15个字节,按照8字节对齐,补充1个字节就好了,调整原始的结构体变量顺序
可以看到把占地最大的变量放到最上面之后,可以避免无效的字节补齐,一定程度上节省了内存。
 
如果完全不想要结构体对齐呢,有两个编译器设置
  1. __attribute__((packed))
    1. 关闭编译器的字节对齐,相当于1字节对齐了,结构体的的总大小就是各个元素大小总和。
  1. __attribute__((aligned(n)))
    1. 显时设置n字节对齐,如果结构体中存在大于n的变量,则会以最大的这个大小进行对齐。
       
修改最开始的结构体,然后打印结构体大小
PackedExample 的大小现在就是只有15字节,不会有任何的优化。
Aligned4Example 的大小仍然是24,因为虽然是4字节,但是最长的c变量长度是8字节,所以最终还是按照8字节对齐。
PackedAndAligned4Example 做了一点修改,删掉了最后的变量d,可以看到a,b变量之后都没有补充字节,但是在最后变量之后,由于需要对齐到4字节,所以补充了1字节,让最终的总大小变成12。
所以当两个attribute同时使用时,只会在结构体的末尾根据设置的字节进行对齐补充,这是和只有对齐时的区别。
 
也可以使用#pragma预编译指令,具体使用方法是
通过#pragma pack(n) 表示需要编译器对宏范围内的结构体进行n字节对齐#pragma packed() 作为结束,打印修改后的结构体大小和偏移量
size:12 offset b:2 offset c:4
可以看到总大小是12,只补充了1个字节,但是会发现和PackedAndAligned4Example 的最大区别在于对结构体成员的处理,PackedAndAligned4Example 中的成员之后不会补充字节,这个例子中仍然是在变量a之后补充了一个字节,这样前一个成员始终对齐了后一个成员的大小,并且这个成员补齐后的最终大小不会大于n。

总结

编译器默认会根据目标平台对结构体进行字节对齐,这会让结构体的实际大小更大,所以在设计结构体的时候,需要考虑好这一点,避免无意义的结构体增大,也提供了不同的编译器设置来显式控制对齐
  1. __attribute__((packed))
    1. 不对结构体进行字节对齐
  1. __attribute__((aligned(n)))
    1. 显式指定结构体对齐的字节大小,但是会受结构体中最大成员的影响,同时在和__attribute__((packed)) 一起使用时,只会在结构体末尾补充字节
  1. #pragma pack(n)#pragma packed
    1. 显式指定结构体对齐的字节数,并且不会受最大大小成员的影响,并且补充字节的规则和默认情况一样,会在每个有需要的成员之后补充。

相关资料


© Chen 2021 - 2025