1:对齐规则
struct Example {
char a; // 1字节,存放在0地址
int b; // 4字节,需对齐到4的倍数地址(存放于4-7地址)
short c; // 2字节,对齐到2的倍数地址(存放于8-9地址)
};
这个结构体实际占用 12 字节(而非 1+4+2=7 字节),因为 char 和 int 之间有 3 字节的填充(padding),结构体末尾可能也有填充以满足整体对齐要求。(后面会讲具体计算方法)
不同编译器和平台可能有不同的默认对齐规则,可通过编译指令(如#pragma pack
)调整对齐方式,但不当的调整可能导致性能下降或兼容性问题。
2:对齐值和结构体实际所占内存的区别与联系
对齐值和结构体所占内存是两个紧密相关但又不同的概念,它们共同描述了结构体在内存中的布局特性。
核心区别:
概念 | 定义 | 单位 | 特点 |
对齐值 | 结构体成员或整个结构体在内存中存放时的地址规则(必须是某个值的整数倍)。 | 字节 | 是一种地址约束规则,描述 “如何存放”,不直接表示占用空间大小。 |
结构体所占内存 | 结构体在内存中实际占据的总字节数(包括成员大小和填充字节)。 | 字节 | 是实际占用的空间量,描述 “占用多少”,由成员大小和对齐规则共同决定。 |
联系:
对齐值是计算结构体所占内存的核心依据,两者通过 “填充字节(padding)” 关联:
结构体成员的对齐值决定了成员之间是否需要填充字节(为了满足地址约束)。
整个结构体的总大小必须是其最大成员对齐值的整数倍(整体对齐规则),因此结构体末尾可能需要额外填充。
这里举个例子来分别计算结构体的对齐值和内存:
struct Demo {
char x; // 对齐值1字节,存于地址0
int y; // 对齐值4字节,需从地址4开始(0-3之间填充3字节)
short z; // 对齐值2字节,从地址8开始(占用8-9)
};
成员对齐值:x=1
,y=4
,z=2
,结构体整体对齐值 = 最大成员对齐值 = 4。
结构体所占内存:从0开始,char x占了一个字节,存于地址0,int y占四个字节,由于每个成员的起始地址必须是其自身对齐值(通常等于自身大小)的整数倍。,所以它从地址4开始存储,占了四个字节,到地址7。由于地址8本身就是 short z的对齐值2的倍数,所以可以直接从地址8存储。最后结构体占用到了地址9,由于整个结构体的总大小必须是其最大成员对齐值的整数倍,所以结构体占了12个字节。
3:为什么要存在内存对齐
那在设计结构体的时候,我们既要满⾜对⻬,⼜要节省空间,如何做到:
让占⽤空间⼩的成员尽量集中在⼀起
1 //例如:
2 struct S1
3 {
4 char c1;
5 int i;
6 char c2;
7 };
8
9 struct S2
10 {
11 char c1;
12 char c2;
13 int i;
14 };
4:结构体嵌套
#include <stdio.h>
// 内部结构体
struct Inner {
char a; // 1字节,对齐值1
int b; // 4字节,对齐值4
}; // 内部结构体大小:1 + 3(填充) + 4 = 8字节(最大成员对齐值4,8是4的倍数)
// 外部结构体(嵌套了Inner)
struct Outer {
short x; // 2字节,对齐值2
struct Inner y; // 嵌套的结构体,对齐值=其内部最大成员对齐值4
char z; // 1字节,对齐值1
};
int main() {
printf("Inner size: %zu\n", sizeof(struct Inner)); // 输出8
printf("Outer size: %zu\n", sizeof(struct Outer)); // 输出16
return 0;
}
内存布局分析:
-
内部结构体
struct Inner
的布局:char a
占用地址 0(满足 1 字节对齐)- 填充 3 字节(地址 1-3)
int b
占用地址 4-7(满足 4 字节对齐)- 总大小 8 字节(是最大对齐值 4 的倍数)
-
外部结构体
struct Outer
的布局:short x
占用地址 0-1(满足 2 字节对齐)- 填充 2 字节(地址 2-3),因为接下来的
struct Inner y
需要 4 字节对齐 struct Inner y
占用地址 4-11(共 8 字节,满足 4 字节对齐)char z
占用地址 12(满足 1 字节对齐)- 填充 3 字节(地址 13-15),使总大小为 16 字节(外部结构体最大对齐值是 4,16 是 4 的倍数)
关键规则:
- 嵌套的结构体成员,其对齐值等于自身内部最大成员的对齐值(而非结构体本身的大小)
- 外部结构体的总大小必须是所有成员(包括嵌套结构体)的最大对齐值的整数倍
- 嵌套结构体内部的填充和外部的填充是相互独立的,都会影响最终总大小
这个例子中,外部结构体因为嵌套了需要 4 字节对齐的内部结构体,最终总大小被 “拉齐” 到 16 字节,而不是简单的 2+8+1=11 字节。
5:修改默认对齐数
#include <stdio.h>
#pragma pack(1)//设置默认对⻬数为1
struct S
{
char c1;
int i;
char c2;
};
#pragma pack()//取消设置的对⻬数,还原为默认
int main()
{
//输出的结果是什么?
printf("%d\n", sizeof(struct S));
return 0;
}