# 02.结构体内存对齐

## 一、各类型所占内存对照表

![1](/files/-MbiH7E2yO0OonjU63bO)

## 二、结构体内存大小

### 2.1 创建调试用结构体

```C++
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 调试输出结构体大小

```C++
(lldb) po sizeof(Misa)
24

(lldb) po sizeof(Light)
16

(lldb) po sizeof(Together)
56
```

### 2.3 思考

奇怪了，`Misa`和`Light`，明明内存成员相同，怎么一个16，一个24呢？下面我们先了解一个东西：`内存对齐`

## 三、内存对齐规则

### 3.1 数据成员对齐规则

* 第一个数据成员从偏移量为0的地方开始，占用该成员大小的字节。
  * 如Misa，以`double`开始，那么就是`0-7`
* 以后每个成员的存储起始位置都要从`该成员大小`或者`成员的子成员`大小的`整数倍`开始
  * 子成员，如：
    * 数组、结构体等

### 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变量的总长度至少能容纳最大的成员变量，而且要满足是所有成员变量类型大小的整数倍

举个例子：游戏中的控制角色移动

```C++
union Dirction {
  bool up;
  bool down;
  bool left;
  bool right;
} move;
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ryukiedev.gitbook.io/wiki/ios/di-ceng/02.-jie-gou-ti-nei-cun-dui-qi.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
