结构体属于一种自定义类型,它是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。
结构体的自引用
结构体的成员也可以是结构体,当然,如果它的成员是其本身,则不能直接引用:
struct S
{
int a;
struct S s;
};
发生编译错误:
其实想想也能明白,这样做的后果会导致该结构体类型的大小无法确定。
因此正确的自引用方法应为:
struct S
{
int a;
struct S* s;
};
通过地址来找到该结构体变量,其大小也能确定。
而这种自引用一般在链表中使用:
结构体的内存对齐
由于结构体类型属于自定义类型,因此其大小应该是根据其具体内容来确定的。
结构体的大小并不是简单的把所有类型相加,而是利用一定的对齐规则来存储,其具体规则如下:
1. 第一个成员在与结构体变量偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
VS中默认的值为8
3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
struct data
{
int a;
char b;
long c;
double d;
short e;
};
譬如说这样一个结构体类型,根据前述三条规则可以大致画出其存储方式:
也就是说内存大小应为32。
事实也确实如此。
当然,如果结构体中嵌套了结构体,则需要额外考虑规则4。
接着来讲讲为什么存在内存对齐。
以我个人的理解来说,计算机在读取一段数据时很难根据其类型不同而更改读取方式,譬如说:
struct S
{
int a;
char b;
short c;
int d;
};
像这样一个结构体类型,如果不存在内存对齐,而是按照这种方式来存储:
在读取数据时,计算机一次读取的内存大小应该是固定的,假如说每次读取4个字节的内存大小,
那要读到int d这个数据就要读取两次才能得到。读取效率大大降低。而如果采用内存对齐的方式,其存储应该是这样的:
这时读取时就不会存在读取一个数据需要读取两次的情况了。
因此结构体存在内存对齐,是一种用空间换取时间的做法。