结构体内存对齐
结构体内存对齐
为什么存在内存对齐
1.平台原因(移植原因)
不是所有的硬件都能任意访问任意地址上的任意数据的,某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出异常
2.性能原因
数据结构(尤其是栈)应该尽可能的在自然边界上对齐,为了访问未对齐的内存,CPU需要访问内存2次;访问对齐后的内存,CPU只需要访问内存1次。
在Windows的VS中默认对齐值为8
Linux中(GCC编译器)默认对齐值为4
在内存中我们一般读取数据不是按内存来读取,一般都是按内存块来读取。
未对齐的情况下,当需要访问int类型的数据时,需要CPU访问2次内存块(内存块1和内存块2)
对齐的情况下,当访问int类型的数据时,只需要CPU访问1次内存块(内存块2)即可
是一种空间换时间的做法
结构体内存对齐规则
- 第一个成员在结构体变量偏移量为0的地址
- 有效对齐值为Min(编译器默认对齐值,结构体中成员数据类型中占用内存最大值)
- 其他成员要对齐到**Min(有效对齐值,该成员数据类型的内存大小)**的整数倍
- ⚠结构体总大小为有效对齐值的整数倍
- 如果嵌套结构体,有效对齐值为Min(编译器默认对齐值,Max(嵌套结构体中成员数据类型中占用内存最大值,结构体中成员一数据类型占用内存、结构体中成员二数据类型占用内存……结构体中成员N数据类型占用内存))
- 如果嵌套共同体,有效对齐值为Min(编译器默认对齐值,Max( Max(共同体内成员一、共同体内成员二……共同体内成员N),结构体中成员一数据类型占用内存**、结构体中成员二数据类型占用内存……结构体中成员N数据类型占用内存))**
- 共用体所占内存大小:Max(共同体内成员一、共同体内成员二……共同体内成员N***)***
举例
#pramga pack(n)
告诉编译器字节对齐方式为n字节对齐
#pramga pack(8)
struct s1{
char a;
int b;
char c;
};
结构体中最大的成员数据类型占用4字节,有效对齐值为Min(8,4)=4;char为第一个成员,应在结构体偏移量为0的地址,占用1字节;int占用4字节,要对齐在Min(8,4)=4的整数倍位置,即4的位置(4,5,6,7);char占用1字节,要对齐在Min(8,1)=1的整数倍位置,即8位置;因为结构体总大小为有效对齐值的整数倍,所以结构体的大小应为12(0~11)
#pramga pack(8)
struct s1{
char a;
int b;
char c;
};
struct s2{
char a1;
struct s1 S1; //嵌套结构体
double d;
};
结构体s1中最大数据类型结构为int占用4字节,结构体s2的有效对齐值为Min(8,Max(4,1,8))=8;char为第一个成员,应在结构体偏移量为0的地址,占用1字节;嵌套结构体s1(占用12字节)要对齐在Min(8,4)=4的整数倍,即4(4-15);double占用8字节,要对齐在Min(8,8)的整数倍位置,即16(16-23);因为结构体总大小为有效对齐值的整数倍;此时结构体内存大小为24,满足要求。
#pramga pack(4)
struct s1{
char a;
int b;
union w{
int c;
double d;
}w;
short e;
};
共同体w中最大数据类型占用内存为Max(4,8)=8;结构体s1的有效对齐值为Min(4,Max(8,1,4,2))= 4;char为第一个成员,应在结构体偏移量为0的地址,占用1字节;int占用4字节,要对齐在Min(4,4)=4的整数倍位置,即4的位置(4,5,6,7);共同体w占Max(4,8)=8字节,要对齐在Min(4,12)=4的整数倍位置,即8的位置(8-15);short占2字节,要对齐在Min(4,2)=2的整数倍为止,即16(16-17);因为**结构体总大小为有效对齐值的整数倍**;所以结构体s1的大小需要补位到4的整数倍,即20字节。