结构体中的内存对齐是用空间换时间的一种内存操作。
一.结构体对齐的规则
1、数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。
2、结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。
3、结合1、2可推断:当#pragma pack的n值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任何效果。
Win32平台下的微软C编译器(cl.exefor 80×86)的对齐策略:
1)结构体变量的首地址是其最长基本类型成员的整数倍;
备注:编译器在给结构体开辟空间时,首先找到结构体中最宽的基本数据类型,然后寻找内存地址能是该基本数据类型的整倍的位置,作为结构体的首地址。将这个最宽的基本数据类型的大小作为上面介绍的对齐模数。
2)结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
备注:为结构体的一个成员开辟空间之前,编译器首先检查预开辟空间的首地址相对于结构体首地址的偏移是否是本成员的整数倍,若是,则存放本成员,反之,则在本成员和上一个成员之间填充一定的字节,以达到整数倍的要求,也就是将预开辟空间的首地址后移几个字节。
3)结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要,编译器会在最末一个成员之后加上填充字节(trailing padding)。
备注:结构体总大小是包括填充字节,最后一个成员满足上面两条以外,还必须满足第三条,否则就必须在最后填充几个字节以达到本条要求。
4) 结构体内类型相同的连续元素将在连续的空间内,和数组一样。
5) 如果结构体内存在长度大于处理器位数的元素,那么就以处理器的倍数为对齐单位;否则,如果结构体内的元素的长度都小于处理器的倍数的时候,便以结构体里面最长的数据元素为对齐单位。
二.例子说明规则
struct A
{
char c; //1 8 1
int i; //4 8 4
double b; //8 8 8
};
int main()
{
printf("%d\n", sizeof(struct A ));
system("pause");
return 0;
}
上面的代码会产生什么样的结果?为什么?
1.首先将结构体的第一个数据成员放在0偏移除(0,占用1个字节),下来判断1偏移处是不是第二个成员的对齐数(4)的整数倍,不是,向后偏移3个字节(0-3,占用4个字节)偏移到第二个成员的对齐数(4)的1倍处,将int型数据填充,向后偏移4个字节(0-7,占用8个字节),判断8偏移处是不是第三个成员的对齐数(8)的整数倍,是,直接填充(0-15,占用16个字节)。判断16 是不是所有结构体成员的最大对齐数的整数倍(16是8的整数倍,不用向后面填充)。
#pragma pack(4)
struct A
{
char c;//1 4 1
int i; //4 4 4
double b; //8 4 4
};
#pragma pack()
int main()
{
printf("%d\n", sizeof(struct A));
system("pause");
return 0;
}
上述代码又是什么结果?
1.虽然结果是一样的,但是系统的默认对齐数被改成了4。
2.一定要在算内存对齐这类题的时候注意默认对齐数!!
typedef struct A
{
double b;//8 8 8 0-7
int i;//4 8 4 0-11
short s;//2 8 2 0-13
char c;//1 8 1 0-14
//一共15个字节不是最大对齐数的整数倍,向后填充到8的整数倍 所以为16
}A;
typedef struct B
{
char c;//1 8 1 0-3
int i; //4 8 4 0-7
A a;
//判断当前偏移处是不是结构体A最大对齐数的整数倍,不是向后偏移到(8)的整数倍!!!此程序是,所以不用偏移,直接填充。
//0-23 占用24个字节 判断24 是不是两个结构体最大对齐数(8)的整数倍。24 是 8 的整数倍,所以不用偏移。
}B;
int main()
{
printf("%d", sizeof(B));
system("pause");
return 0;
}
结构体中嵌套结构体在 c和c++ 中有差异。在c中不管被嵌套的结构体是否创建对象,占用字节数都是将被嵌套结构体所占用的字节数加上。在c++中,如果被嵌套的结构体创建了对象,将不用追加字节,如果被嵌套的结构体没有创建对象,则需要追加字节数,具体规则,和上面例子一样!
以上就是本人在学习过程中的一些经验总结。当然,本人能力有限,难免会有纰漏,希望大家可以指正。
转载于:https://blog.51cto.com/10799170/1718679