结构体内存对齐,也就是计算结构体的大小
一、结构体内存对齐规则
1 ,结构体的第一个成员放在结构体变量在内存中存储位置的0偏移处开始
2 ,从第2个成员往后的所有成员,都放在一个对齐数(成员的大小和默认对齐数地较小值)的整数的整数倍地址处
3, 结构体的总大小是结构体的所有成员的对齐数中最大的那个对齐数的整数倍
4, 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
二、结构体内存对齐规则解析
1,结构体内的成员,无论是int,char或者其他类型的数据,在内存中存储的位置都从0偏移处开始。
给一个结构体作为示例:
struct s
{
char a;
int b;
char c;
};
该结构体的第一个成员为 char 类型的数据,char 类型的数据在内存中占据一个字节的大小,所以该数据存储的位置为0偏移处开始,一个字节的存储空间。如图所示:
2, 从第二个成员开始,都放在一个对齐数(成员的大小和默认对齐数地较小值)的整数倍地址处,这是什么意思呢?我们用一个结构体来举例,由于这里使用的是vs编译器,默认对齐数为8。
找出结构体中每个成员变量的大小,将其与编译器的默认对齐数进行比较,找到其中的较小值:
我们可以看到第二个成员的对齐数为4,所以应该对齐到位置偏移为4的倍数处,即第一个成员对齐数为偏移0处,第二个 int 类型成员要对齐到位置偏移4处,因为int类型占4个字节大小,所以第二个char类型的成员应该对齐到位置偏移8处,如图所示:
3, 结构体的总大小是结构体的所有成员的对齐数中最大的那个对齐数的整数倍
我们可以看到该结构体内 int 类型的成员为所有成员的对齐数中最大的那个对齐数,所以结构体的总大小应该是 int 类型较小值4的整数倍,但是由上图可知,该结构体在内存中只占据了九个字节的位置,这时候我们应该怎么办?答案是:补齐!
将第三个成员后的变量进行补齐,满足4的整数倍的条件,如图所示:
因此,这个结构体的总大小为:12
4, 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
这里我们用另一个结构体表示,即:
struct s
{
char a;
int b;
char c;
};
struct s1
{
char d;
struct s;
int e;
};
在结构体s1中嵌套了结构体s,结构体s1同样遵守上面说的三个规则,到了第二个成员变量为s的情况下,就要满足第4个规则,即:结构体s中自己的最大对齐数为int类型变量的4个字节,所以结构体s应该对齐到4的倍数的偏移处去,如图所示:
而结构体s的大小就是占据变量的大小,第三个成员为int类型,位置应该为4的整数倍地址处,如图所示:
这时候结构体的大小为20,满足规则4:整数倍于结构体成员最大对齐数(包括嵌套结构体对齐数),结构体s1的成员最大对齐数为 int 类型的4个字节,因此该结构体大小为:20!
三、结构体内存对齐案例分析
给出一个示例结构体s:
struct s
{
char a;
double b;
int c;
};
按照上述的规则进行计算结构体大小,如图所示:
该结构体成员的最大对齐数为:8,而现在只占了20个字节,不满足最大对齐数的整数倍,因此需要进行补齐,最后该结构体的大小为:24!
第二个例子结构体为:
struct s
{
char a;
double b;
int c;
};
struct s1
{
char d;
struct s;
int e;
};
此时变量大小为36,结构体s1的成员最大对齐数为8(结构体s的 double 类型),不满足规则,因此需要进行补齐,结构体s1大小为:40!