上一篇:【内存对齐】第四篇·Array、Union内存对齐的规律与原则
首先,定义规则的人应该希望:前面各种构造类型的规则,如果可以递归,就可以用于嵌套的构造类型。所以我们只需要猜想并进行验证即可。根据构造类型的排列组合,我们可以将嵌套方式分为如下几种:
外层类型 | 内层类型 |
---|---|
数组 | 结构体 |
共用体 | 结构体 |
结构体 | 结构体 |
结构体 | 数组 |
数组 | 数组 |
共用体 | 数组 |
结构体 | 共用体 |
数组 | 共用体 |
共用体 | 共用体 |
本篇介绍前3种的对齐规则。
数组 { 结构体 } = 结构体数组
结构体作为元素构成的数组,被称为结构体数组。即有一个数组,数组中的每个元素都是一个结构体。那么其对应的每个元素都应该是对齐的,整个数组的大小等于“结构体大小*元素个数”。
struct {char; char; char; }
test[1] Size: 3 Address: 0x00000001004071a4
test[2] Size: 6 Address: 0x00000001004071a4
test[3] Size: 9 Address: 0x00000001004071a8
struct {char; short; }
test[1] Size: 4 Address: 0x00000001004071a4
后面不用再多验证了,结论是:
a. 结构体数组Size=结构体元素元整后的Size*元素个数。
b. 结构体数组的起始Address规则同数组本身规则。
共用体 { 结构体 }
共用体中有成员是结构体,猜测是其Size以最大的成员Size为标准,首地址以Size为标准:
union test_u{
char grass;
char tree;
short leaf;
struct flower {
char color;
};
}test;
Size: 2 Address: 0x00000001004071a2
union test_u{
char grass;
char tree;
short leaf;
struct flower {
char color;
int flavor;
};
}test;
Size: 8 Address: 0x00000001004071a8
union test_u{
char grass;
char tree;
short leaf;
struct flower {
char color;
char flavor;
char type;
};
}test;
Size: 4 Address: 0x00000001004071a4
union test_u{
char grass;
char tree;
char leaf;
struct flower {
char color;
char flavor;
char type;
char habbit;
char lifespan;
};
}test;
Size: 5 Address: 0x00000001004071a4
union test_u{
char grass;
char tree;
int leaf;
struct flower {
char color;
char flavor;
char type;
char habbit;
char lifespan;
};
}test;
Size: 8 Address: 0x00000001004071a8
union test_u{
char grass;
char tree;
int leaf;
struct flower {
char color;
char flavor;
char lifespan;
};
}test;
Size: 3 Address: 0x00000001004071a4
这里出现了一个非常有意思的现象,联合体的大小要大于等于其中的结构体的大小,且为最大成员类型大小的整数倍。所以结论是:
a. 联合体的大小是大于等于 [所有成员中 [最小的 [最大非结构体成员大小的整数倍]]]。
(非常拗口,可以分解为 联合体的大小满足: 1.大于等于所有成员;2. 是所有非结构体成员的整数倍)
b. 联合体的首地址是和Size有关的,规则同联合体本身的规则。
结构体 { 结构体 }
结构体与共用体不一样,没有“共用“,所以可能会有
A. 被嵌套的结构体的大小相当于内部所有结构体展开后,再计算的大小;
或者
B. 内部结构体先做一次计算,再参与到被嵌套结构体的计算中。
实践得真知:
struct test_st{
char meat;
char vegetable;
struct fruit{
char color;
};
}test;
Size: 3 Address: 0x00000001004071a4
struct test_st{
char meat;
char vegetable;
struct fruit{
char color;
char flavor;
char smell;
};
}test;
Size: 5 Address: 0x00000001004071a4
struct test_st{
char meat;
char vegetable;
struct fruit{
char color;
short flavor;
};
}test;
Size: 6 Address: 0x00000001004071a4
内层结构体的size之前已经论述过,应该是4,所以6=1+1+4,说明结构体的成员并没有按照内层结构体的总大小来对齐(否则应为8)。但是否做了展开,并按照short进行对齐了呢?
struct test_st{
char meat;
short vegetable;
char egg;
struct fruit{
char color;
int flavor;
};
}test;
Size: 16 Address: 0x00000001004071b0
根据之前几节的分析,可知内层结构体的size是8,这里假设没有展开,其他结构体成员的大小应该是按照short的大小2对齐,这样size=2+2+2+8 = 14,显然和计算结果不符。
那假设这里做了展开,也就是所有成员按照int的大小4来对齐,那就是2+2+4+8=16,恰好是计算出的结果。这样就清晰了:
结构体嵌套结构体的对齐原则,是按照所有内外层元素中最大宽度的类型来对齐的,相当于将结构体嵌套做展开。
这里还做了另一个实验,为了避免出现结构体表述产生的差异:
struct fruit_st{
char color;
int flavor;
};
struct test_st{
char meat;
short vegetable;
char egg;
struct fruit_st fruit;
}test;
Size: 16 Address: 0x00000001004071b0
可以看到,结论是一样的。
还有一点需要验证,当结构体成员的大小大于内部嵌套的结构体的大小的时候,会如何圆整?
struct fruit_st{
char color;
};
struct test_st{
char meat;
int egg;
struct fruit_st fruit;
}test;
Size: 12 Address: 0x00000001004071a8
所以可以得到结论:
a. 结构体中嵌套结构体,其结构体的Size是相当于将内层结构体展开后,重新组成的整个结构体进行Size的计算。(这里有明显的Recursive适用性)
b. 结构体嵌套结构体,其起始地址与Size相关,其规则与结构体本身规则相同。
连载中… by 2021/08/28