C语言中常见的数据类型有char int short long float double 等,这些类型都成为C语言中的内置类型,但是这些内置类型在表达时都很单一,然而实际上我们需要描述的很多复杂对象仅是一个单一的类型无法清晰的描述与表达,于是有了自定义类型来帮助我们对复杂对象进行描述
今天先分析一下自定义类型中结构体,位段和联合在内存中的存储细节
1.结构体的内存对齐
在计算结构体大小时,我们就需要谈到结构体的内存存储细节,结构体内的多种不同数据类型合在一起究竟是怎么存储的?单一的内置类型和放在结构体中时,他们所占的内存还会是相同的嘛?
先看一段代码
#include<stdio.h>
struct s1
{
char c1;//1
int i;//4
char c2;//1
};
struct s2
{
char c1;//1
char c2;//1
int i;//4
};
int main()
{
printf("%d\n",sizeof(struct s1);
printf("%d\n",sizeof(struct s2);
return 0;
}
这里我们看到s1和s2可以尝试分析一下他们的大小
从平常的思维来看 结构体大小可能是对应着每个不同的数据类型所占的长度将其相加。
但是实际运行出来的结果:结构体s1的大小为12byte 结构体s2的大小为8byte
为什么会是这样的结果呢?
实际内存所占空间为什么远远超过了原来的大小
因为要算得结构体的大小,我们需要知道什么一个知识点叫结构体对齐
这里我们介绍详细介绍一下结构体对齐的规则:
1.第一个成员在与结构体变量偏移量为0的地址处。
2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。对齐数=编译器默认的一个对齐数与该成员大小的较小值
vS中默认的值为8 gcc等编译器上没有默认对齐数 (没有就默认是成员自身大小)
3.结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
以上4点为结构体对齐的规则,我们在计算结构体大小时,需要严格遵守规则计算
根据对齐规则我们可以形象的从图片中了解到每一个数据类型的对齐方式
c1和c2 的对齐数为1 而int的对齐数为4 因此c1和c2都要与4对齐 同时结构体大小必须是最大对齐数的整数倍 4+8+4=12byte
而s2的排序方式与s1不同 导致其结构体大小也发生改变
当c1对齐最大对齐数4开辟了4个内存空间时,c2在能够满足存储时,并未多开辟内存
从这两个例子可以发现结构体的大小计算方式
总体来说:结构体内存对齐的做法是拿空间来换时间的做法
总结:设计结构体的时候,我们既要满足对齐,又要节省空间,尽量把小的类型放在一起和前面,从而规避空间的浪费
2.位段
位段的内存分配
1.位段的成员可以是int unsigned int signed int或者是char(属于整形家族)类型
⒉.位段的空间上是按照需要以4个字节( int)或者1个字节( char)的方式来开辟的
3.位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。
#include<stdio.h>
struct A
{
//4byte-32bit (上来先开辟32个bit位)
int _a : 2; //后面的数字是bit位 int类型为32个bit位 但是这里不需要32个bit位,只需要2个bit 因此使用位段来重新定义其所占的bit位
int _b : 5;
int _c : 10;
//32bit到此还剩15bit
//于是又开辟4个byte-32bit
int _d : 30;
};
//看起来只占用47bit
//实际占用64bit(虽然还是浪费了 但是知足常乐 )
int main()
{
printf("%d\n",sizeof(struct A));//8byte
return 0;
}
位段的内存分配较为简单,具体分配方式已在代码中展示
3.联合
联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。
#include<stdio.h>
union Un{ //合租的感觉 a和c共用一块空间
int a;//4
char c;//1
};
struct st{ //独立房间的感觉 a和c都为自己分别开开辟一块空间 因此会产生多余的空间
int a;//4
char c;//1
};
int main()
{
union Un u;
printf("%d\n",sizeof(u));//4
printf("%d\n",sizeof(struct st));//8
return 0;
}
联合的内存存储也是较为简单 具体方式已在代码中展示
以上内容简单分析了自定义类型的内存存储细节,如有不妥,直接指出