想要计算结构体,我们要先了解结构体的对齐规则:
1. 第一个成员在与结构体变量偏移量为 0 的地址处。2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。对齐数 = 编译器默认的一个对齐数 与 该成员大小的 较小值 。VS 中默认的值为 8(由于编译环境的不同,对其值会有所不同)3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的 整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
注:
我们可以使用 #pragma pack 这一预处理指令改变编译器的默认对其数,通过 #pragma pack(n)
,可以将对齐方式设置为 n 字节,其中 n 可以是 1、2、4、8 或其他大于 1 的值。
下面是一个示例:
#pragma pack(8) //设置默认对齐数为 8 个字节
struct Test {
char a;
int b;
double c;
};
#pragma pack() // 恢复默认对齐方式
int main() {
// ...
return 0;
}
我们在使用预处理指令后恢复默认对齐方式,这样不会对后续代码造成影响。
接下来我们来通过几个例题详细探讨下结构体内存的对齐方式以及结构体大小的计算。
(注意:本节所有程序均在VS编译器下运行,默认对齐数为8字节。我们也可以通过 #pragma pack(8) 来设定默认对齐数为8。)
例一:
struct S1 { char c1; // 对齐数为 1 int i; // 对齐数为 4 char c2; // 对齐数为 1 };
第一步:结构体中第一个成员 c1 在偏移量为0处。
第二步:找到 int 类型对齐数整数倍数处的地址,储存 int 型变量。
第三步:找到 char 类型对齐数整数倍数处的地址,储存 char 型变量。
第四步:找到结构体类型中的最大对齐数,本题为 4 ,结构体大小即为最大对齐数的倍数,大小为 12 。
具体步骤见下图图解:
下面是计算结构体大小的图解:
我们再来了解如何计算嵌套结构体的大小:
我们来复习下嵌套结构体的对齐规则:
如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
例2:
struct S3
{
double d; // 对齐数为 8
char c; // 对齐数为 1
int i; // 对齐数为 4
};
struct S4
{
char c1; // 对齐数为 1
struct S3 s3; // 对齐数为结构体s3中的最大对齐数,即为 8
double d; // 对齐数为 8
};
本题目中,结构体 s4 中嵌套了 结构体 s3,我们如何计算出 s4 的大小呢。
我们了解到 s3 中最大对齐数为8,所以 s3 应从地址为8的倍数处对齐。同时 s4 中最大对齐数为8,所以最终s4的大小也应为8的倍数。
同时,嵌套结构体中其他变量仍遵循对齐准则。
下面我们来看图解:
首先我们需要计算 s3 的结构体大小,方法同例一:
接下来我们来计算 s4: