C++结构体与共用体大小计算及内存对齐规则详解
C++中的结构体和共用体在内存布局上遵循严格的对齐规则。本文通过代码对比展示不同成员排序对结构体大小的影响,解释对齐数与最大对齐数的概念,并详细拆解结构体、共用体及枚举类型的内存大小计算方法,帮助开发者优化内存使用。
一、结构体内存对齐规则
结构体的内存大小不仅取决于成员的总和,还受到内存对齐规则的显著影响。在实际开发或面试中,理解如何通过代码验证和调整结构体成员顺序至关重要。
第1步:基础结构体大小验证
目的:观察相同数据类型但不同排列顺序的结构体所占内存差异。
我们定义四个包含字符、整型及数组的结构体 Node1 至 Node4,代码如下:
#include <stdio.h>
struct Node1{
char c1;
int val1;
char c2;
};
struct Node2{
char c1;
char c2;
int val1;
};
struct Node3{
char c1;
char array[10];
};
struct Node4{
char val1;
int arrar[10];
};
int main(){
printf("Node1 size = %d\n",sizeof(struct Node1));
printf("Node2 size = %d\n",sizeof(struct Node2));
printf("Node3 size = %d\n",sizeof(struct Node3));
printf("Node4 size = %d\n",sizeof(struct Node4));
return 0;
}
运行上述代码后,得到的内存大小结果如下:
第2步:明确对齐数与最大对齐数
要正确计算结构体大小,首先需要掌握两个核心概念:
- 对齐数:结构体中每个成员的对齐倍数。例如,
char的对齐数为 1,int的对齐数为 4。对于数组,其对齐数是其内部元素类型的大小,而非数组总长度。因此在Node3中,对齐数为 {1, 1};Node4中对齐数为 {1, 4}。 - 最大对齐数:结构体内部所有对齐数中的最大值。该值通常受编译器限制(如 GCC 通常为 4,VS 为 8)。计算时取“结构体最大对齐数”与“编译器对齐数”的较小值。本例以 GCC 为例,最大对齐数即结构体内部的数值最大值。
第3步:结构体内存分配逻辑与计算
结构体内存分配的核心原则是:结构体总大小必须是最大对齐数的整数倍。
当存放成员变量时,为了保证连续性,如果当前剩余空间不足以存放下一个成员(且未达到对齐边界),编译器会填充字节(Padding),使下一个成员从对齐倍数位置开始存储。这虽然浪费了少量内存,但提高了 CPU 读取效率。
基于上述原理,各结构体具体计算方式如下:
- Node1 (12 字节):
c1(1) + 填充3 + val1(4) + c2(1) + 填充3 = 12。 - Node2 (8 字节):
c1(1) + c2(1) + 填充2 + val1(4) = 8。(紧凑排列节省空间) - Node3 (11 字节):
c1(1) + array(10) = 11。 - Node4 (44 字节):
val1(1) + 填充3 + array[10](4*10=40) = 44。
第4步:嵌套结构体的大小计算
当结构体内部包含其他结构体时,嵌套结构体的对齐数被视为其自身的最大对齐数。
#include <stdio.h>
struct Node1{
char c1;
int val1;
char c2;
};
struct Node2{
char c1;
struct Node1 node;
double val1;
};
int main(){
printf("Node1 size = %d\n",sizeof(struct Node1));
printf("Node2 size = %d\n",sizeof(struct Node2));
return 0;
}
在此案例中:
Node1的最大对齐数为 4,大小为 12。Node2包含char,Node1(相当于int),double。其成员对齐数为 {1, 4, 8},因此最大对齐数为 8。
Node2 的具体内存分布为:
c1(1) + 填充7(确保接下来的node从 8 的倍数地址开始)。node(12) + 填充4(16是8的倍数,此处需补足至8的倍数,实际上node占12,加上前面的偏移量,这里为了后续double的对齐可能需要考虑整体结构,具体按标准补齐)。
根据常见对齐规则推算:Node2 = 1 (c1) + 7 (Pad) + 12 (node) + 4 (Pad,用于使 double 对齐,或者整体最终大小需对齐到8的倍数) + 8 (double) = 24 字节。最终结果为:
- sizeof(Node1) = 12
- sizeof(Node2) = 24
二、共用体(Union)内存大小计算
共用体的成员共享同一块内存区域。这意味着共用体的大小至少应能容纳最大的成员变量,同时也必须遵循内存对齐原则。
第5步:共用体示例验证
#include <stdio.h>
union un1{
int val;
char c;
double d;
};
union un2{
int val;
char array[5];
};
int main(){
printf("un1 size = %d\n",sizeof(union un1));
printf("un2 size = %d\n",sizeof(union un2));
return 0;
}
运行结果如下:
分析:
- un1:最大成员是
double(8字节)。其自身也是 8 的倍数,故大小为 8。 - un2:成员有
int(4字节) 和char[5](5字节),最大占用为 5 字节。但是,int的对齐要求决定了整个共用体的对齐数为 4。共用体总大小必须是最大对齐数(4)的整数倍。大于等于 5 且能被 4 整除的最小数字是 8。
三、枚举(Enum)的大小
除了结构体和共用体,枚举类型的大小也常引起混淆。其大小并非固定,而是由编译器根据赋值的范围决定。
第6步:枚举大小验证
#include <stdio.h>
enum Colour {
RED,
GREEN,
BLUE
};
enum ProgramLanguage {
python = 0xffffffffff,
c = 8,
java
};
int main()
{
printf("Colour size = %d\n",sizeof(enum Colour));
printf("ProgramLanguage size = %d\n",sizeof(enum ProgramLanguage));
return 0;
}
运行结果如下:
结果说明:
Colour 只使用了默认的小整数,编译器通常赋予其 int 的类型,故大小为 4 字节。而 ProgramLanguage 中包含了极大的数值 0xffffffffff(十六进制),超出了 32位 int 的范围,迫使编译器将其提升为更大尺寸(通常是 64位 long long),故大小变为 8 字节。这说明枚举的实际占用完全取决于具体的赋值情况。
总结
掌握 C++ 结构体的内存对齐、共用体的共享机制以及枚举的动态类型分配,能够有效避免内存泄漏或越界访问风险,并在底层编程中实现最优的资源利用率。以上就是C++结构体字节对齐和共用体大小计算的详细内容,更多关于C++内存管理机制的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













