C语言结构体内存的大小会根据不同的对齐数进行不同的计算,也会因为结构体成员的排列顺序影响最终的结构体内存大小。为了解决这一难题,本文给出一些较为常见的解法来帮助对结构体内存计算有困难的同学或爱好者,欢迎各位大佬进行批评指正。
数据类型 | char | short | int | long | long long | float | double |
---|---|---|---|---|---|---|---|
大小(字节) | 1 | 2 | 4 | 4 | 8 | 4 | 8 |
结构体内存计算规则:
1.基本对齐单位:每个成员的起始地址必须是其大小的整数倍
2.结构体总大小:必须是最大对齐数的整数倍
最大对齐数:结构体成员变量中最大基础数据类型的大小与编译器默认对齐数中的较小值,即两者中小的那个即为最大对齐数,不过多数编译器都没有这个默认选项,但VS 2013的默认最大对齐数为8个字节。
struct s
{
char a;
char b;
short c;
int d;
};
在VS 2013中,结构体s中占据最大内存空间大小的数据类型是int,在32位操作系统中占4个字节,而VS 2013的默认对齐数为8个字节,两者对比可以知道最大对齐数为4个字节。
计算步骤为:
1.确定结构体中各个成员变量所占的内存空间大小
2.根据每个成员变量所占内存空间的大小计算其相对结构体起始地址的偏移量,即每个变量的基本对齐单位
3.计算当前结构体所占内存空间的总大小
4.计算结构体的最大对齐数
5.结构体最终的空间大小必须为结构体最大对齐数的整数倍
若步骤3中的总大小是最大对齐数的整数倍,则这个数就是最终的结果,否则将填充至最大对齐数的整数倍。
typedef struct s
{
char a; // 1 字节
int b; // 4 字节
char c; // 1 字节
short d; // 2 字节
}s;
int main()
{
s s1 = {0};
printf("结构体的空间大小为:%d字节\n",sizeof(s1));
return 0;
}
结构体的空间大小为:12字节
计算步骤为:
1、结构体s中各个成员变量所占的空间大小分别为:a占1个字节,b占4个字节,c占1个字节,d占2个字节
2.1、a是char类型的数据,其相对结构体起始地址的偏移量为0(1的整数倍)
2.2、b是int类型的数据,其相对结构体起始地址的偏移量为4(4的整数倍)
2.3、c是char类型的数据,其相对结构体起始地址的偏移量为8(1的整数倍)
2.4、d是short类型的数据,其相对结构体起始地址的偏移量为10(2的整数倍)
3、当前结构体所占内存空间的总大小 = 4 + 4 + 2 + 2 = 12个字节
4、结构体中最大的基础数据类型是int(4个字节),固最大对齐数是4个字节
5、结构体最终空间大小 = 12(12是4的整数倍,若步骤3中的结果为11,则需填充至12字节)
步骤3中各个数字的解释:
4(a的偏移量是0,占1个字节,b(int类型)占4个字节,其偏移量是4的整数倍,固偏移量1~3位置处将被填充)
4(b的偏移量是4,占4个字节)
2(c的偏移量是8,占1个字节,d(short类型)占2个字节,其偏移量是2的整数倍,固偏移量9位置处将被填充)
2(d的偏移量是10,占2个字节)
为了便于理解,下面给出内存的示例图。(其中变量a是结构体s中定义的第一个成员)
1、成员变量a占1个字节的空间大小,偏移量是1的整数倍,即偏移量为0,图中红色框选的内容;
2、成员变量b占4个字节的空间大小,偏移量是4的整数倍,即偏移量为4,图中绿色框选的内容;
3、成员变量c占1个字节的空间大小,偏移量是1的整数倍,即偏移量为8,图中蓝色框选的内容;
4、成员变量d占2个字节的空间大小,偏移量是2的整数倍,即偏移量为10,图中紫色框选的内容;
其中偏移量1~3和偏移量9位置处的内存空间被浪费了,但也是结构体占据的内存空间。偏移量0~11是结构体目前占据的内存空间,最大对齐数是4个字节,结构体最终的空间大小为4的整数倍,即这里的12个字节。
可以看出,上述定义的结构体类型存在4个内存空间的浪费,所以设计者应综合考虑内存空间的布局从而设计出性价比最高的定义。下面给出几种较为合理的结构体定义。
// 第一种定义
typedef struct s
{
char a; // 1 字节
char c; // 1 字节
short d; // 2 字节
int b; // 4 字节
}s;
// 第二种定义
typedef struct s
{
short d; // 2 字节
char a; // 1 字节
char c; // 1 字节
int b; // 4 字节
}s;
// 第三种定义
typedef struct s
{
int b; // 4 字节
char a; // 1 字节
char c; // 1 字节
short d; // 2 字节
}s;
结构体的空间大小为:8字节
从结果可知,赶紧后的结构体占据的空间大小为8字节,和占据12个字节空间存储的内容是一样的,但不同的定义方式造成的结构体占据空间不同。为了便于理解,这里以第二种定义为例画出它的内存示例图。
以下是3道练习题,计算步骤与案例相同,请大家按照步骤逐步求解。建议在基础阶段借助内存示例图辅助计算,熟练后便可在脑中直接构建布局得出结果。
//1、习题1
struct s
{
char arr[10]; // 10 字节
int b; // 4 字节
short c; // 2 字节
};
//答案:20字节
//2、习题2
struct s
{
char a; // 1 字节
int b[3]; // 12 字节
short c[4]; // 8 字节
};
//答案:24字节
//3、习题3
struct s
{
char a; // 1 字节
int b[2]; // 8 字节
short c; // 2 字节
float d; // 4 字节
double e; // 8 字节
};
//答案:32字节
结构体套结构体内存计算规则:
1.基本对齐单位:每个成员的起始地址必须是其大小的整数倍
2.结构体总大小:必须是最大对齐数的整数倍
3.嵌套结构体处理:嵌套结构体的起始地址必须是其最大基础数据类型成员大小的整数倍,其内部成员仍按自身规则对齐
struct s1
{
char c; // 1字节
short a; // 2字节
int i; // 4字节
};
struct s2
{
short s; // 2字节
struct s1 st; // 嵌套结构体
double d; // 8字节
};
int main() {
printf("s1 size: %d bytes\n", sizeof(struct s1));
printf("s2 size: %d bytes\n", sizeof(struct s2));
return 0;
}
s1 size: 8 bytes
s2 size: 24 bytes
计算步骤为:
1.1、结构体s1中各个成员变量所占的空间大小分别为:c占1个字节,a占2个字节,i占4个字节
1.1、结构体s2中各个成员变量所占的空间大小分别为:s占2个字节,st占8个字节,d占8个字节
2.1、s是short类型的数据,相对结构体s2起始地址的偏移量为0(2的整数倍)
2.2、st是嵌套结构体,内部最大基础数据类型是int(4字节),相对结构体s2起始地址的偏移量为4(4的整数倍)
2.3、d是double类型的数据,相对结构体s2起始地址的偏移量为16(8的整数倍)
3、当前结构体所占内存空间的总大小 = 4 + 12 + 8 = 24个字节
4、结构体s2中最大的基础数据类型是double(8个字节),固最大对齐数是8个字节
5、结构体s2最终空间大小 = 24(24是8的整数倍)
步骤3中各个数字的解释:
4(s的偏移量是0,占2个字节,st(嵌套结构体)占8个字节,偏移量是4的整数倍,偏移量2~3位置处将被填充)
12(st的偏移量是4,占8个字节,d(double类型)占8个字节,偏移量是8的整数倍,偏移量12~15位置处被填充)
8(d的偏移量是16,占8个字节)
为了便于理解,下面给出内存的示例图。(其中变量s是结构体s2中定义的第一个成员)
1、成员变量s占2个字节的空间大小,偏移量是2的整数倍,即偏移量为0,图中红色框选的内容;
2、成员变量st占8个字节的空间大小,偏移量是4的整数倍,即偏移量为4,图中绿色框选的内容;
2.1、成员变量c占1个字节的空间大小,偏移量是1的整数倍,即偏移量为0,图中橙色框选的内容;
2.2、成员变量a占2个字节的空间大小,偏移量是2的整数倍,即偏移量为2,图中黄色框选的内容;
2.3、成员变量i占4个字节的空间大小,偏移量是4的整数倍,即偏移量为4,图中粉色框选的内容;
3、成员变量d占8个字节的空间大小,偏移量是8的整数倍,即偏移量为16,图中蓝色框选的内容;
和1.1的案例一样,结构体s2的设计存在空间浪费,其中偏移量2~3和偏移量12~15位置处的内存空间被浪费了。偏移量0~23是结构体s2目前占据的内存空间,最大对齐数是8个字节,结构体s2最终的空间大小为8的整数倍,即24个字节。
struct s1
{
short c[2]; // 4字节
int a; // 4字节
double i; // 8字节
};
struct s2
{
char s[8]; // 8字节
struct s1 st; // 嵌套结构体
short d[4]; // 8字节
};
int main() {
printf("s1 size: %d bytes\n", sizeof(struct s1));
printf("s2 size: %d bytes\n", sizeof(struct s2));
return 0;
}
s1 size: 16 bytes
s2 size: 32 bytes
计算步骤为:
1.1、结构体s1中各个成员变量所占的空间大小分别为:数组c占4个字节,a占4个字节,i占8个字节
1.1、结构体s2中各个成员变量所占的空间大小分别为:数组s占8个字节,st占16个字节,数组d占8个字节
2.1、s是char类型的数组,相对结构体s2起始地址的偏移量为0(1的整数倍)
2.2、st是嵌套结构体,内部最大的数据类型是double(8字节),相对结构体s2起始地址的偏移量为8(8的整数倍)
2.3、d是short类型的数组,相对结构体s2起始地址的偏移量为24(2的整数倍)
3、当前结构体所占内存空间的总大小 = 8 + 16 + 8 = 32个字节
4、结构体s2中最大的数据类型是结构体s1中的double(8个字节),固最大对齐数是8个字节
5、结构体s2最终空间大小 = 32(32是8的整数倍)
步骤3中各个数字的解释:
8(s的偏移量是0,占8个字节),16(st的偏移量是8,占16个字节),8(d的偏移量是24,占8个字节)
由于篇幅有限,就不给出内存的示例图。(其中变量s是结构体s2中定义的第一个成员)
1、成员变量s占8个字节的空间大小,偏移量是1的整数倍,即偏移量为0
2、成员变量st占16个字节的空间大小,偏移量是8的整数倍,即偏移量为8(结构体s1的起始地址)
2.1、成员变量c占4个字节的空间大小,偏移量是2的整数倍,即偏移量为0(实际上是8+0)
2.2、成员变量a占4个字节的空间大小,偏移量是4的整数倍,即偏移量为4(实际上是8+4)
2.3、成员变量i占8个字节的空间大小,偏移量是8的整数倍,即偏移量为8(实际上是8+8)
3、成员变量d占8个字节的空间大小,偏移量是2的整数倍,即偏移量为24
偏移量0~31是结构体s2目前占据的内存空间,最大对齐数是8个字节,结构体s2最终的空间大小为8的整数倍,即32个字节。
以下是2道练习题,计算步骤与案例相同,请大家按照步骤逐步求解。建议在基础阶段借助内存示例图辅助计算,熟练后便可在脑中直接构建布局得出结果。
//1、习题1
struct Inner
{
char a; // 1字节
int arr[3]; // 12字节
char c; // 1字节
short d; // 2字节
};
struct Outer
{
short s; // 2字节
struct Inner inner; // 嵌套结构体
double d; // 8字节
};
//答案:Inner size = 20 bytes,Outer size = 32 bytes
//2、习题2
struct Inner1 {
char a; // 1字节
int arr[3]; // 12字节 (3*4)
char c; // 1字节
short d; // 2字节
double e; // 8字节
};
struct Inner2 {
int arr[4]; // 16字节 (4*4)
float c; // 4字节
short d; // 2字节
double e; // 8字节
};
struct Outer {
short s; // 2字节
struct Inner1 inner1; // 嵌套结构体
double d; // 8字节
char e[5]; // 5字节
struct Inner2 inner2; // 嵌套结构体
};
//答案:Inner1 size = 32 bytes,Inner2 size = 32 bytes,Outer size = 88 bytes