一、结构体类型
1.认识结构体
结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。
如何创建一个结构体呢,如下代码便创建了一个结构体
struct abc//abc是这个结构体的名字 { int a;//这个a,b,c是结构体的成员列表 char b; float c; }abc1; //abc1为创建的一个该结构体类型的变量,你也可以在别的地方定义这个变量,因为你已经创建了一个新的类型,一个新的结构体类型
2.如何使用结构体类型
创建完了一个结构体类型,那我们应该这样去使用这个结构体类型呢?
2.1按顺序初始化结构体,并通过.符号访问
#include<stdio.h> struct abc//abc是这个结构体的名字 { int a;//这个a,b,c是结构体的成员列表 char b; float c; }abc1; //abc1为创建的一个该结构体类型的变量,你也可以在别的地方定义这个变量,因为你已经创建了一个新的类型,一个新的结构体类型 int main() { struct abc a1 = { 20,'b',5.2 };//按顺序初始化结构体 //struct abc 此时就是声明类型可类比成int printf("%d %c %f\n", a1.a, a1.b, a1.c); //将内容打印出来,用.这个符号直接访问结构体变量的成员 }
2.2无序初始化结构体,并用.符号访问
#include<stdio.h> struct abc//abc是这个结构体的名字 { int a;//这个a,b,c是结构体的成员列表 char b; float c; }abc1; //abc1为创建的一个该结构体类型的变量,你也可以在别的地方定义这个变量,因为你已经创建了一个新的类型,一个新的结构体类型 int main() { struct abc a2 = {.b='c',.a=10,.c=3.14};//通过.符号来无序的初始化结构体变量 printf("%d %c %f\n", a2.a, a2.b, a2.c);//将内容打印出来 }
2.3通过地址的方式访问结构体变量成员
#include<stdio.h> struct abc//abc是这个结构体的名字 { int a;//这个a,b,c是结构体的成员列表 char b; float c; }abc1; //abc1为创建的一个该结构体类型的变量,你也可以在别的地方定义这个变量,因为你已经创建了一个新的类型,一个新的结构体类型 int main() { struct abc a3 = { .a = 60,.c = 9.9,.b = 'a' };//初始化结构体变量 struct abc* address = &a3; //创建一个名字为address的struct abc类型的结构体指针来储存a3这个结构体变量的地址 printf("%d %c %f\n", address->a,address->b,address->c); //通过->访问地址的方式将内容打印出来 }
2.4结构体的自引用
2.4.1错误的自引用
#include<stdio.h> struct abc { int a; struct abc next;//结构体里存放一个结构体 };
小明觉得在结构体里存放一个结构体,这样子创建出多个结构体后,就能将多个结构体存放到一个结构体中,一环扣一环,通过一个结构体访问所有的结构体,这听上去好像还有几分道理,但你要明白,这个结构体里面,还有一个这个类型的结构体,而这个被包含的结构体里面也有,被包含的结构体里面的那个结构体也有......子子孙孙无穷尽也,可以类比成死递归,没有人知道什么时候穷尽,所以计算机该给它分配多少个字节的空间,计算机根本不知道,所以这很显然是错误的。
2.4.2正确的自引用
#include<stdio.h> struct abc { int a; struct abc* a1; //结构体里存放一个结构体的地址,而被存放的结构体也能存放一个结构体的地址 //那么有一个结构体,便可链接所有这个结构体类型的变量 //实现一个访问所有 };
只要你是个地址你所占字节的大小不是4(32位)就是8(64位),因此计算机可以分配给它空间,这个结构体和上面那个错误的结构体最大的区别就是一个存放的是地址,一个存放的是内容
3.结构体内存对齐
话不多说,先上题目
#include<stdio.h> struct abc { char a; int b; }; int main() { printf("%d", sizeof(struct abc)); }
你不妨猜测一下打印出来的结果是多少,之前没遇到过这种题型的宝子们,答案应该就是5吧,char占一个字节,int占4个字节,瞧瞧多合理啊,但结果是
是8,没想到吧,你的心里现在一定有个大大的疑惑,为什么呢?
这就要说到结构体对齐的问题了,首先我们要知道结构体对齐后长什么样才能够计算出结构体的正确大小
3.1结构体对齐的规则
(1) 第一个成员在与结构体变量偏移量为0的地址处。
(2) 其他成员变量要对齐到某个对齐数的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数与该成员大小的较小值。
VS中默认的值为8
(3) 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
(4) 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
有的小伙伴恐怕不知道什么是偏移量,针对结构体的情况,偏移量就是相对于你所创建的结构体首地址的距离,比方说第一天规则,第一个成员要在偏移量为0的地址处,偏移量为0,那么很显然,就是没偏移嘛,就是在首地址存放。所以第一条规则我们可以直接立即理解为第一个成员放首地址,非常的符合常识。
后面的三条讲解起来太抽象了,这里画个图给大家
假设这是我存放结构体的内存空间,图中的数字为偏移量
按照第一条规则先把char a放进去,占一个字节的大小,那么我们接下来放int,根据第二条规则,int类型的大小为4,相比vs默认的8较小,所以它的对齐数应为4,故我们得找到偏移量为4的倍数的位置,并在那开始存储int b,因此4,5,6,7偏移量的位置就被int b所占据了,1,2,3则是被用来被浪费掉了,故打印出来的结果是8