一,结构体的定义
(1)常规方式
结构体内可以定义各种元素,包括:常规变量(字符,整形,浮点),数组,指针,以及结构体,其中结构体有多种的定义方式,以下是结构体的三种常规定义方式。
(2)匿名结构体
当结构体在声明时 Struct后面不再有结构体名字时,称为匿名结构体,此时需要在结构体最后加上结构体变量的名字,(声明,由于只有定义结构体的名字,因此该结构体没有变量名字,因此该类型结构体为一次性结构体,只能通过定义的名字使用一次)
其中由于进行指针操作需要两个相同的变量类型,但两个不同的结构体不能进行指针操作,因为不同的结构体被看作为不同的变量类型。
如上图所示,不可以进行 *psa = sa。因为两个不同的结构体是两个不同的变量类型。
(3)结构体的自引用
//结构体指针自引用的正确用法
struct A
{
int a;
sturct *A next;
}
如上在链表结构中使用的结构体自引用,在结构体自引用时需注意如果结构体通过匿名定义,则不可以使用自引用,根据编译器的执行逻辑,在运行到结构体时,还未定义NODE*,因此指针找不到NODE*.
(4)结构体变量的初始化
二,结构体的内存对齐
结构体在内存中的位置与内存对齐有着关联,其中内存对齐要根据对齐数来进行判断,如某编译器的默认对齐数为8,int型大小为4,则结构体的成员变量要对其到4的整数倍地址处,char则需要对其到1的整数倍地址处。而一个结构体的最大容量为成员间最大对齐数的整数倍,在嵌套了一个结构体时,则需要对其到嵌套结构体内的最大对其数的整数倍处,最大容量为嵌套结构体的最大对齐数的整数倍。(其中数组的最大对齐数不是数组的存储容量,而是数组的类型的对齐数例如 char i[【】为1 int i【】为4)
Q:为什么要使用结构体的内存对齐?
本质上是使用牺牲空间换取时间的一种做法,因为在数据线每次访问内存时,为了提高数据线的访问效率,让内存线通过整数倍的方式来进行访问
tips:通过代码的写法也可以节省空间,如下图所示
s1结构体变量:char首地址比较后1个对齐数,int比较后四个对齐数,char比较后一个对齐数,因此100011111000使用了12个字节(1代表使用的空间,0代表浪费的空间)
s1结构体变量:char首地址比较后1个对齐数,char比较后一个对齐数,int比较后四个对齐数,因此11001111使用了8个字节(1代表使用的空间,0代表浪费的空间)
修改对齐数
通过#pragma修改默认对齐数位,可以进一步节省空间
offsetof(计算结构体首位置编译量的宏)分析结构体中的成员地址的首地址距离结构体首地址的距离,单位字节。