1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
2) 结构体每个成员相对结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节{trailing padding}。
对于以上规则的说明如下:
第一条:编译器在给结构体开辟空间时,首先找到结构体中最宽的基本数据类型,然后寻找内存地址能被该基本数据类型所整除的位置,作为结构体的首地址。将这个最宽的基本数据类型的大小作为上面介绍的对齐模数。
第二条:为结构体的一个成员开辟空间之前,编译器首先检查预开辟空间的首地址相对于结构体首地址的偏移是否是本成员大小的整数倍,若是,则存放本成员,反之,则在本成员和上一个成员之间填充一定的字节,以达到整数倍的要求,也就是将预开辟空间的首地址后移几个字节。
第三条:结构体总大小是包括填充字节,最后一个成员满足上面两条以外,还必须满足第三条,否则就必须在最后填充几个字节以达到本条要求。
【例2】假设4字节对齐,以下程序的输出结果是多少?
1 /* OFFSET宏定义可取得指定结构体某成员在结构体内部的偏移 */ 2 #define OFFSET(st, field) (size_t)&(((st*)0)->field) 3 typedef struct{ 4 char a; 5 short b; 6 char c; 7 int d; 8 char e[3]; 9 }T_Test; 10 11 int main(void){ 12 printf("Size = %d\n a-%d, b-%d, c-%d, d-%d\n e[0]-%d, e[1]-%d, e[2]-%d\n", 13 sizeof(T_Test), OFFSET(T_Test, a), OFFSET(T_Test, b), 14 OFFSET(T_Test, c), OFFSET(T_Test, d), OFFSET(T_Test, e[0]), 15 OFFSET(T_Test, e[1]),OFFSET(T_Test, e[2])); 16 return 0; 17 }
执行后输出如下:
1 Size = 16 2 a-0, b-2, c-4, d-8 3 e[0]-12, e[1]-13, e[2]-14
下面来具体分析:
首先char a占用1个字节,没问题。
short b本身占用2个字节,根据上面准则2,需要在b和a之间填充1个字节。
char c占用1个字节,没问题。
int d本身占用4个字节,根据准则2,需要在d和c之间填充3个字节。
char e[3];本身占用3个字节,根据原则3,需要在其后补充1个字节。
因此,sizeof(T_Test) = 1 + 1 + 2 + 1 + 3 + 4 + 3 + 1 = 16字节。