深入解析 C 语言中含数组和指针的构造体与共同体内存计算
在 C 语言中,构造体(
struct
)和共同体(union
)允许我们将多种数据类型组合到一起。除了常见的基本数据类型之外,经常还会在它们中嵌入数组和指针。由于数组的内存是连续分配的,而指针的大小与平台相关(32 位一般为 4 字节,64 位一般为 8 字节),计算内存大小时就需要特别注意内存对齐和填充的影响。本文将通过具体示例说明如何计算包含数组和指针的构造体和共同体的内存大小。
一、构造体(Struct)中包含数组和指针
1.1基本原理
- 数组:数组内存占用空间为“数组元素个数×单个元素大小”,且所有元素是连续排列。
- 指针:指针只存储一个地址,其大小固定(取决编译平台,与指向数据无关)。
- 内存对齐与填充:编译器为了提高内存访问效率,会按照每个成员的对齐要求进行排列。如果某个成员的起始地址不满足对齐要求,编译器会在前面插入填充字节。同时整个结构体的大小也会被调整为结构体中最大对齐要求的整倍数。
1.2示例: 构造体包含数组和指针
假设在 64 位系统下(指针大小为 8 字节,int
为 4 字节,char
为 1 字节),如下构造体的定义为例:
struct Example {
char a; // 1 字节
int b; // 4 字节,要求 4 字节对齐
char arr[3]; // 数组,占 3 字节
int *ptr; // 指针,占 8 字节(64 位系统)
};
内存布局分析
1.成员 a
(char):
- 从偏移 0 开始,占 1 字节。
2.成员 b
(int):
- 由于
int
要求 4 字节对齐,而a
只占 1 字节,所以在a
后面需要 3 字节填充,使得b
的起始地址为偏移 4。 - b 占 4 字节,从偏移 4 到 7。
3.成员 arr[3]
(数组):
- 紧接在
b
后面,从偏移 8 开始。 - 数组大小为 3 字节,覆盖偏移 8、9、10。
4.成员 ptr
(指针):
- 指针要求 8 字节对齐,而当前下一个可用偏移为 11,不满足 8 字节对齐(因为 11 不是 8 的倍数)。
- 编译器需要在
arr
后插入 5 字节填充,使得ptr
从偏移 16 开始。 ptr
占 8 字节,从偏移 16 到 23。
5.整体大小调整:
- 当前各部分占用总字节数为 24 字节(0~23),而最大对齐要求为 8 字节,24 已经是 8 的倍数,因此最终结构体大小为 24 字节。
1.3 嵌套构造体中包含数组和指针
假设在 64 位系统下(指针大小为 8 字节,int
为 4 字节,char
为 1 字节),如下构造体的定义为例:
struct Example {
char a; // 1 字节
int b; // 4 字节,要求 4 字节对齐
char arr[3]; // 数组,占 3 字节
int *ptr; // 指针,占 8 字节(64 位系统)
};
内存布局分析
1.成员 a
(char):
- 从偏移 0 开始,占 1 字节。
2.成员 b
(int):
- 由于
int
要求 4 字节对齐,而a
只占 1 字节,所以在a
后面需要 3 字节填充,使得b
的起始地址为偏移 4。 b
占 4 字节,从偏移 4 到 7。
3.成员 arr[3]
(数组):
- 紧接在
b
后面,从偏移 8 开始。 - 数组大小为 3 字节,覆盖偏移 8、9、10。
4.成员 ptr
(指针):
- 指针要求 8 字节对齐,而当前下一个可用偏移为 11,不满足 8 字节对齐(因为 11 不是 8 的倍数)。
- 编译器需要在
arr
后插入 5 字节填充,使得ptr
从偏移 16 开始。 ptr
占 8 字节,从偏移 16 到 23。
5.整体大小调整:
- 当前各部分占用总字节数为 24 字节(0~23),而最大对齐要求为 8 字节,24 已经是 8 的倍数,因此最终结构体大小为 24 字节。
二、共同体(union)中包含数组和指针
2.1 基本原理
在共同体中,所有成员共享同一块内存,其大小由最大成员的大小决定,同时也需要满足该成员的对齐要求。即使共同体中包含数组和指针,原则也是一致的。
2.2 示例:共同体中包含数组和指针
union Union {
int arr[4]; // 数组:4 个 int,每个 4 字节,总共 16 字节
double *dptr; // 指针,8 字节(64 位系统)
char c[10]; // 数组:10 个 char,总 10 字节
};
内存大小计算
arr[4]
: 占 4 × 4 = 16 字节dptr
: 占 8 字节c[10]
: 占 10 字节
取最大值: 最大成员为 arr[4]
,大小为 16 字节。
同时需要考虑最大对齐要求,假设 int
要求 4 字节,而指针要求 8 字节;由于最大成员(数组)的元素对齐为 4 字节,但联合体的整体对齐要求通常取决于所有成员中最大的(这里可能由指针决定为 8 字节),不过最终分配空间依旧是 16 字节(且该空间会按 8 字节对齐)。因此,该共同体的总大小为 16 字节。
三、总结与注意事项
1.构造体(struct):
- 内存分布为成员按照声明顺序排列。
- 数组成员按照数组中所有元素总大小分配。
- 指针成员只占指针本身大小,不考虑所指数据。
- 必须考虑每个成员的对齐要求,必要时插入填充字节,整体大小也需调整为最大对齐要求的整数倍。
- 嵌套构造体时,先计算内部结构体的大小,再按照外部成员的排列顺序计算整体大小。
2.共同体(union):
- 所有成员共享同一块内存,大小取决于最大的成员(同时满足对齐要求)。
- 数组和指针的计算方法依然适用,但只取最大值即可。