结构体填充与数据对齐
- 结构变量的大小等于它包含所有变量的总大小。
- 结构体填充:是编译器用来对齐内存偏移数据。
- 字段填充:为了提高性能,编译器在结构体中利用
结构体填充
方法进行数据对齐。 - 数据对齐:当CPU读写内存时,它都在小块内(字长或4个字节)进行。这种安排增加了系统的性能,有效地将数据存放在字长整数倍的偏移地址。
- 结构体中每个数据类型都要对齐
- 联合体中按照最大长度的数据类型对齐
- 按照基本数据类型对齐
为什么要字节对齐?
现代计算机中内存的存储理论上都是按照 byte
大小来存储的,但实际上是按照 字长(word size)
为单位存储的。这样做是减少CPU访问内存的次数,加大CPU访问内存的吞吐量。比如同样读取8个字节的数据,一次读取4个字节那么只需要读取2次。
字节对齐方法
- C编译器中采用
#pragma
指令#pragma pack(n)
编译器将按照n
个字节对齐 _packed
: 按照一字节对齐。packed一般会以降低运行性能为代价,由于大多数cpu处理数据在合适的字节边界数的情况下会更有效,packed的使用会破坏这种自然的边界数。- GCC编译器中采用
attribute((aligned (n)))
方式对齐。让所作用的结构成员对齐在
n
字节自然边界上。如果结构体中有成员的长度大于n
,则按照最大成员的长度来对齐。 - GCC编译器中采用
attribute ((packed))
取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。 - 结构体字节对齐的细节和具体编译器实现相关,但一般而言满足三个准则
- 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
- 结构体每个成员相对结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节
internal adding
; - 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节
trailing padding
。
为什么要用传递结构体指针变量?
结构体中数据成员变量的数据非常大,采用
结构体指针变量
比传递值
的效率要高,花费的时间少。