原文:C语言字节对齐问题详解
结构体字节对齐的细节和具体编译器实现相关,但一般而言满足三个准则:
1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
2) 结构体每个成员相对结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节{trailing padding}。
演示讲解
32位机器上各数据类型的长度为:char为1字节、short为2字节、int为4字节、long为4字节、float为4字节、double为8字节
阴影部分为内存填充
对于以上规则的说明如下:
第一条:编译器在给结构体开辟空间时,首先找到结构体中最宽的基本数据类型,然后寻找内存地址能被该基本数据类型所整除的位置,作为结构体的首地址。将这个最宽的基本数据类型的大小作为上面介绍的对齐模数。
第二条:为结构体的一个成员开辟空间之前,编译器首先检查预开辟空间的首地址相对于结构体首地址的偏移是否是本成员大小的整数倍,若是,则存放本成员,反之,则在本成员和上一个成员之间填充一定的字节,以达到整数倍的要求,也就是将预开辟空间的首地址后移几个字节。
第三条:结构体总大小是包括填充字节,最后一个成员满足上面两条以外,还必须满足第三条,否则就必须在最后填充几个字节以达到本条要求。
示例代码
/* gcc/g++默认采用4字节对齐,当然可以指定对齐方式,如通过: #pragma pack (value)时的指定对齐值value。 */ /* OFFSET宏定义可取得指定结构体某成员在结构体内部的偏移 */ #define OFFSET(st, field) (size_t)&(((st*)0)->field) typedef struct{ char a; short b; char c; int d; char e[3]; }T_Test; int main(void) { printf("Size = %d a-%d, b-%d, c-%d, d-%d e[0]-%d, e[1]-%d, e[2]-%d ", sizeof(T_Test), OFFSET(T_Test, a), OFFSET(T_Test, b), OFFSET(T_Test, c), OFFSET(T_Test, d), OFFSET(T_Test, e[0]), OFFSET(T_Test, e[1]),OFFSET(T_Test, e[2])); return 0; }
如何设置字节对齐方式
#pragma pack(push) //保存对齐状态 #pragma pack(1) //设置为1字节对齐 typedef struct{ char a; short b; char c; int d; char e[3]; }T_Test; #pragma pack(pop) //恢复对齐状态
#pragma pack(1) //设置为1字节对齐 typedef struct{ char a; short b; char c; int d; char e[3]; }T_Test; #pragma pack() //取消指定对齐,恢复缺省对齐
对于本地使用的数据结构,为提高内存访问效率,采用四字节对齐方式;同时为了减少内存的开销,合理安排结构体成员的位置,减少四字节对齐导致的成员之间的空隙,降低内存开销。
对于处理器之间的数据结构,需要保证消息长度不会因不同编译平台或处理器而导致消息结构体长度发生变化,使用一字节对齐方式对消息结构进行紧缩;为保证处理器之间的消息数据结构的内存访问效率,采用字节填充的方式自己对消息中成员进行四字节对齐。
数据结构的成员位置要兼顾成员之间的关系、数据访问效率和空间利用率。顺序安排原则是:四字节的放在最前面,两字节的紧接最后一个四字节成员,一字节紧接最后一个两字节成员,填充字节放在最后。