From ab8fedb1e3cc7569d49af0068eb96b84c4f9f4ca Mon Sep 17 00:00:00 2001 From: matrix Date: Fri, 25 Mar 2022 12:08:47 +0800 Subject: [PATCH] fix error(struct.md): struct memory layout size. --- docs/struct.md | 49 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/docs/struct.md b/docs/struct.md index 0d93f54..611ed1b 100644 --- a/docs/struct.md +++ b/docs/struct.md @@ -147,14 +147,53 @@ numbers[0].denominator = 7; 上面示例声明了一个有1000个成员的数组`numbers`,每个成员都是自定义类型`fraction`的实例。 -struct 结构占用的存储空间,不是各个属性存储空间的总和。因为为了计算效率,C 语言的内存占用空间一般来说,都必须是`int`类型存储空间的倍数。如果`int`类型的存储是4字节,那么 struct 类型的存储空间就总是4的倍数。 - +struct 结构占用的存储空间,不是各个属性存储空间的总和,而是最大内存占用元素的倍数,占位不足的元素会进行对齐填充。比如以64位机器为例。 ```c -struct { char a; int b; } s; -printf("%d\n", sizeof(s)); // 8 +// 占24个字节,sizeof(struct foo) = 24; +struct foo { + int a; + char *b; + char c; +}; ``` +在64位机器中`int a`占4个字节,指针`char *b`类型占8个字节,`char c`占1个字节,实际有效存储空间13字节。但编译器会进行内存对齐填充,即向占用内存空间最大的指针`char *b`对齐,具体做法是空位填充。 +```c +struct foo { + int a; // 4 + char pad1[4]; // 填充4字节 + char *b; // 8 + char c; // 1 + char pad2[7]; // 填充7字节 +}; +``` +为什么浪费这么多内存空间进行内存对齐?这是为了减少CPU的访存次数,毕竟比起高速运转的CPU和寄存器,访存是最耗时间的操作。一般情况让变量的存储地址是它占用字节的倍数,例如让`long`型变量的地址是8的倍数(例如: 0x0008),`int`型变量的地址是4的倍数,`short`型是2的倍数,`char`占一个字节,但一般都会在`char`型变量后进行空字节填充,即使不是在结构体中。这样处理后CPU就可以按照字或者半字寻址,一次访存就定位到变量的起始地址。 -上面示例中,如果按照属性占据的空间相加,变量`s`的存储空间应该是5个字节。但是,struct 结构的存储空间是`int`类型的倍数,所以最后的结果是占据8个字节,`a`属性与`b`属性之间有3个字节的“空洞”。 +由于结构体的这一特性,一般在定义结构体的时候,采用内存占用递减进行元素排序,这样可以比较有效的优化内存占用,原因如下。 +```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字节 +}; +``` ## struct 的复制