1170 字
6 分钟
结构体内存对齐
一、各类型所占内存对照表

二、结构体内存大小
2.1 创建调试用结构体
struct Girl { double height; char firstChar; int age; short hairCount;}Misa;
struct Boy { double height; int age; char firstChar; short hairCount;}Light;
struct Kira { double height; int age; char firstChar; short hairCount; struct Girl misa; struct Boy light;}Together;2.2 调试输出结构体大小
(lldb) po sizeof(Misa)24
(lldb) po sizeof(Light)16
(lldb) po sizeof(Together)562.3 思考
奇怪了,Misa和Light,明明内存成员相同,怎么一个16,一个24呢?下面我们先了解一个东西:内存对齐
三、内存对齐规则
3.1 数据成员对齐规则
- 第一个数据成员从偏移量为0的地方开始,占用该成员大小的字节。
- 如Misa,以
double开始,那么就是0-7
- 如Misa,以
- 以后每个成员的存储起始位置都要从
该成员大小或者成员的子成员大小的整数倍开始- 子成员,如:
- 数组、结构体等
- 子成员,如:
3.2 结构体作为成员对齐规则
- 如果一个
结构体A内,有结构体B作为成员。则该成员要从其内部最大元素大小的整数倍地址开始存储.
3.3 总大小对齐
- 结构体总大小,必须是其内部最大成员的整数倍,不足要补齐。
四、还原内存结构
4.1 Misa的结构
| - | double | char | int | short |
|---|---|---|---|---|
| 大小(字节) | 8 | 1 | 4 | 2 |
| 使用空间 | 0.1.2.3.4.5.6.7 | 8 | 12.13.14.15 | 16.17 |
- double
- 8字节
- 从0开始,占用0-7
- char
- 1字节
- 从1的整数倍开始,8满足条件,占用8
- int
- 4字节
- 从4的整数倍开始,上一个末位为8,那么先后找,就找从12开始,占用12-15
- short
- 2字节
- 从2的整数倍开始,上一个末位为15,那就从16开始,暂用16-17
- 总大小
- 这些成员暂用了0-16共17个字节
- 按照第三条对齐规则,向上取最大成员8的倍数,结果就是0-23,即24字节。和实际输出相同
4.2 Light的结构
| - | double | int | char | short |
|---|---|---|---|---|
| 大小(字节) | 8 | 4 | 1 | 2 |
| 使用空间 | 0.1.2.3.4.5.6.7 | 8.9.10.11 | 12 | 14.15 |
- double
- 8字节
- 从0开始,占用0-7
- int
- 4字节
- 从4的倍数开始,合适的是8,暂用8-11
- char
- 1字节
- 从1的倍数开始,合适的是12,占用12
- short
- 2字节
- 从2的倍数开始,合适的是14,占用14-15
- 总大小
- 成员占用了0-15
- 按照原则3,需要是最大成员8的倍数,即16字节。和实际输出相同
4.3 对比Light和Misa
如此我们可以发现,对于结构体来说,内部成员的顺序直接影响了结构体占用的空间。
五、嵌套结构体Together的内存结构
5.1 Together的外层结构
| - | double | int | char | short | Girl | Boy |
|---|---|---|---|---|---|---|
| 大小(字节) | 8 | 4 | 1 | 2 | 成员最大8 | 成员最大8 |
| 使用空间 | 0.1.2.3.4.5.6.7 | 8.9.10.11 | 12 | 14.15 | … | … |
由于比较长,Girl和Boy我们拿出来做
5.2 Girl
从8的整数倍开始,即16
| - | double | char | int | short |
|---|---|---|---|---|
| 大小(字节) | 8 | 1 | 4 | 2 |
| 使用空间 | 16.17.18.19.20.21.22.23 | 24 | 28.29.30.31 | 32.33 |
5.3 Boy
从8的整数倍开始,即40
| - | double | int | char | short |
|---|---|---|---|---|
| 大小(字节) | 8 | 4 | 1 | 2 |
| 使用空间 | 40.41.42.43.44.45.46.47 | 48.49.50.51 | 52 | 54.55 |
5.4 总size
- 占用0-55
- 最大成员8,则Together总大小为56。与输出一致。
六、思考
6.1 顺序影响大小,我们知道OC对象的本质就是结构体,那么是不是我们在开发的时候要算出最优解,再来排列成员呢?
并不用,这里内部做了优化了的。后面我们会单独对这里进行研究。
6.2 做内存对齐的意义何在呢?
a.平台因素
- 不是所有硬件平台都能访问任意地址上的任意数据的
- 某些硬件平台只能在某些地址处理某些特定的数据类型,否则会抛出硬件异常
b.性能因素
- 数据结构,尤其是栈,应该尽可能的在自然边界上对齐
- 因为未来访问未对齐的内存,处理器需要做两次内存访问;而对其的内存访问只需要一次
c.总结
- 内存对齐是一个
以空间换时间的过程,为了使CPU更高效的读写数据,提升性能
七、联合体
- 结构体
- 优点:所有成员可共存,全面
- 缺点:内存分配存在浪费,不管用不用,全部分配
- 联合体
- 优点:内存使用更加精细灵活,节省空间
- 缺点:成员互斥
- 同时只有一个成员可以得到这块内存的使用权(对该内存的读写),各变量共用一个内存首地址
- 一个union变量的总长度至少能容纳最大的成员变量,而且要满足是所有成员变量类型大小的整数倍
举个例子:游戏中的控制角色移动
union Dirction { bool up; bool down; bool left; bool right;} move;