mirror of
https://github.com/wangdoc/clang-tutorial.git
synced 2026-02-03 19:03:44 +08:00
docs(struct): edit 属性的内存占用
This commit is contained in:
@@ -147,16 +147,22 @@ numbers[0].denominator = 7;
|
||||
|
||||
上面示例声明了一个有1000个成员的数组`numbers`,每个成员都是自定义类型`fraction`的实例。
|
||||
|
||||
struct 结构占用的存储空间,不是各个属性存储空间的总和,而是最大内存占用元素的倍数,占位不足的元素会进行对齐填充。比如以64位机器为例。
|
||||
struct 结构占用的存储空间,不是各个属性存储空间的总和,而是最大内存占用属性的存储空间的倍数,其他属性会添加空位与之对齐。这样可以提高读写效率。
|
||||
|
||||
```c
|
||||
// 占24个字节,sizeof(struct foo) = 24;
|
||||
struct foo {
|
||||
int a;
|
||||
char *b;
|
||||
char* b;
|
||||
char c;
|
||||
};
|
||||
printf("%d\n", sizeof(struct foo)); // 24
|
||||
```
|
||||
在64位机器中`int a`占4个字节,指针`char *b`类型占8个字节,`char c`占1个字节,实际有效存储空间13字节。但编译器会进行内存对齐填充,即向占用内存空间最大的指针`char *b`对齐,具体做法是空位填充。
|
||||
|
||||
上面示例中,`struct foo`有三个属性,在64位计算机上占用的存储空间分别是:`int a`占4个字节,指针`char* b`占8个字节,`char c`占1个字节。它们加起来,一共是13个字节(4 + 8 + 1)。但是实际上,`struct foo`会占用24个字节,原因是它最大的内存占用属性是`char* b`的8个字节,导致其他属性的存储空间也是8个字节,这样才可以对齐,导致整个`struct foo`就是24个字节(8 * 3)。
|
||||
|
||||
多出来的存储空间,都采用空位填充,所以上面的`struct
|
||||
foo`真实的结构其实是下面这样。
|
||||
|
||||
```c
|
||||
struct foo {
|
||||
int a; // 4
|
||||
@@ -165,36 +171,24 @@ struct foo {
|
||||
char c; // 1
|
||||
char pad2[7]; // 填充7字节
|
||||
};
|
||||
printf("%d\n", sizeof(struct foo)); // 24
|
||||
```
|
||||
为什么浪费这么多内存空间进行内存对齐?这是为了减少CPU的访存次数,毕竟比起高速运转的CPU和寄存器,访存是最耗时间的操作。一般情况让变量的存储地址是它占用字节的倍数,例如让`long`型变量的地址是8的倍数(例如: 0x0008),`int`型变量的地址是4的倍数,`short`型是2的倍数,`char`占一个字节,但一般都会在`char`型变量后进行空字节填充,即使不是在结构体中。这样处理后CPU就可以按照字或者半字寻址,一次访存就定位到变量的起始地址。
|
||||
|
||||
由于结构体的这一特性,一般在定义结构体的时候,采用内存占用递减进行元素排序,这样可以比较有效的优化内存占用,原因如下。
|
||||
为什么浪费这么多空间进行内存对齐呢?这是为了加快读写速度,把内存占用划分成等长的区块,就可以快速在 Struct 结构体中定位到每个属性的起始地址。
|
||||
|
||||
由于这个特性,在有必要的情况下,定义 Struct 结构体时,可以采用存储空间递减的顺序,定义每个属性,这样就能节省一些空间。
|
||||
|
||||
```c
|
||||
// 占24个字节,sizeof(struct foo) = 24;
|
||||
struct foo {
|
||||
int a;
|
||||
char *b;
|
||||
char c;
|
||||
};
|
||||
|
||||
// 内存占用递减排序元素,占16个字节,sizeof(struct foo) = 16;
|
||||
struct foo {
|
||||
char *b;
|
||||
int a;
|
||||
char c;
|
||||
};
|
||||
```
|
||||
上边代码,改变了结构体中`char *b`和`int a`的顺序,内存占用就从24字节下降到16字节,因为此时编译器只需要进行尾填充即可。
|
||||
```c
|
||||
// 占16个字节,sizeof(struct foo) = 24;
|
||||
struct foo {
|
||||
char *b; // 8
|
||||
int a; // 4
|
||||
char c; // 1
|
||||
char pad[3]; // 填充3字节
|
||||
char c;
|
||||
int a;
|
||||
char* b;
|
||||
};
|
||||
printf("%d\n", sizeof(struct foo)); // 16
|
||||
```
|
||||
|
||||
上面示例中,占用空间最小的`char c`排在第一位,其次是`int a`,占用空间最大的`char* b`排在最后。整个`strct foo`的内存占用就从24字节下降到16字节。
|
||||
|
||||
## struct 的复制
|
||||
|
||||
struct 变量可以使用赋值运算符(`=`),复制给另一个变量,这时会生成一个全新的副本。系统会分配一块新的内存空间,大小与原来的变量相同,把每个属性都复制过去,即原样生成了一份数据。这一点跟数组的复制不一样,务必小心。
|
||||
|
||||
Reference in New Issue
Block a user