前言:结构体中的数据储存方式并非像数组那样是在内存中连续储存的,而是存在一个“对齐数”来使数据比较规整的存放在空间中。
struct stu
{
char name;
int age;
char sex;
};
这里会出现两个名词“偏移量”和“对齐数”,下面在实例中给大家解释
首先name会储存在结构体地址开始的首个字节处
其次,age的对齐数为4
判断对齐数大小很简单,对齐数是数据类型大小(因为是int,这里为4)和默认对齐数(vs编译器下是8)的最小值,因此这里的对齐数为4。
从第二个数据开始,数据的首个首个字节的地址的偏移量要为对齐数的整数倍数
因此这里应该是从偏移量为4的地方开始储存(意味着前面3个字节的空间浪费掉),4个字节连续占了偏移量分别为4,5,6,7的位置。
第三个数据是char类型,由上述规则可知道,其对齐数为1
因此可以存放在偏移量为8的地方
最后,还需注意的是结构体大小的分配,虽然上述只涉及9个字节的空间,但是结构体大小是所有成员的对齐数中的最大值的整数倍数。上面几个数据中,最大的对齐数为4,因此结构体空间大小应该为12
当结构体成员有数组时候的情况
struct stu
{
char name[10];
int age[2];
char sex[10];
};
这里的char name[10]可以看成10个连续的char类型数据储存,第一个char类型数据存放在偏移量为0处的地方,然后依次储存偏移量为1,2,3,4,5,6,7,8,9的空间处
对于int类型的数组同理,第一个int类型的数据其对齐数为4,因此会从偏移量为12(因为12是4的倍数)的空间处开始储存,连续占4个字节,分别是偏移量为12,13,14,15,然后第二个int类型的数据会从偏移量为16的地方开始储存连续占四个字节,分别是16,17,18,19。
第三个数组同理从偏移量为20的地方开始,连续占了10个地址
好了,现在计算结构体总大小,最大对齐数为4(是由int类型的数据带来的),因此结构体大小为32。
这里我只画出了结构体的一部分,相信后面我不画出来大家也能懂!
当结构体中有另一个结构体的情况
struct book
{
char naem;
int price;
}BOOK;
struct stu
{
char name;
int age;
char sex;
struct BOOK BOOK;
};
我们很容易知道BOOK的大小为8个字节,以及除去BOOK的第二个结构体的大小为12。但是,第二个结构体的大小并非两者简单相加。
通过前面的知识我们可以知道,char sex对应的偏移量是8。
而BOOK需要从其结构体成员内部数据的最大对齐数的整数倍的偏移量处开始,这里是4,因此应该从移量为12处开始储存。于是占了12,13,14,15,16,17,18,19偏移量处的空间。
最后计算结构体大小,结构体大小为所有成员的对齐数以及BOOK中所有成员范围内最大对齐数的最小整数倍的数字,这里为20。
另外,C语言中还有#program pack()这个宏可以修改默认对齐数,大家感兴趣可以自行研究,结构体储存法则依旧按照上述规则。
位段
struct
{
char a : 3;//为a分配两个比特位
char b : 4;//为b分配五个比特位
char c : 5;//为c分配十个比特位
char d : 4;//为d分配三十个比特位
};
因为是char类型,每次会开辟一个字节的空间,a和b在空间中是连续储存的
但是c占了5个字节,那么前面剩下的一个字节是否要利用呢?
这个在不同编译器下会得到不同的编译结果
不过在VS下这个剩下的是不会利用的。
因此剩下的c和d分别都会开辟一个空间