一 . 结构体的相关概念
结构体,无需多言,是我们的老朋友了,我们之前就学习过一些有关结构体的知识,今天我们就来正式认识一下这个朋友
结构体属于一种自定义类型,在我们C语言中:自定义类型并非只有结构体这一种,还有联合体,枚举。今天我们着重来了解一下结构体
有的小伙伴可能发现了,结构体和我们学过的数组很像,的确是这样的,但是它俩又有着一些区别
数组:一个或者多个成员,元素类型必须相同
结构体:一个或者多个成员,元素类型可以不同
二 . 结构体类型的声明
(1)结构体的一般声明
那么我们结构体是怎样声明的呢?格式如下:
如图,大括号上方,struct tag 就是我们自定义的结构体类型,大括号里的就是成员列表,大括号下面的就是变量列表,我们的结构体便是由这三个部分组成。光看这个可能有些枯燥难懂,我们可以将其运用到实际例子当中去:
如图,咱们要用一个结构体来表示一本书的各项信息,我们就可以这样,成员列表里就是我们需要表示的这本书的各项信息:书名、作者、单价、编号等等。我们的结构体变量一次性可以不止一个,列如我这里的 b1、b2。由于结构体的声明是脱离我们的主函数 —— main()函数之外的,所以这里我们创建的结构体变量自然而然就是一个全局变量
这个时候就有同学会问了,那我们可不可以创建一个局部变量的结构体变量呢?答案是当然可以的,我们还有一种方法是当我们结构体在 main 函数内初始化的时候,这个初始化的同时也会创建结构体变量:
如图,我们在创建结构体类型的时候创建了两个变量:b1、b2,但并未初始化;我们又在 main 函数内创建了变量:b3、b4 并且初始化
由图可见,我在进行结构体的初始化时,用力两种方法,一种是我们正常的初始化,这个初始化必须是严格按照我们创建结构体成员列表时的顺序一对一进行初始化的;第二种方法就不想上面那样必须一个萝卜一个坑的去对照,我们只需要打出一个 . ,然后去选择我们相应的成员去进行初始化就完了
有图可知,我在打印结构体的时候也用了两种方式:
1 . 一种就是我们普通以 . 的方式
2 . 另一种则是我们指针的形式,我们将取出 b4 的地址赋给我们创建的结构体指针变量 ptr,然后在打印时用 “ ptr —> ”的方式,
注意:诸君可以发现,我们打印出来的单价并不是跟我们设定的价格一模一样,而是有一点误差,因为浮点数在内存中有可能是不能精确保存的,因此我们对于浮点数的比较一般也不用 “ = ”, 在上一章节咱们讲解过浮点数在内存中的存储,我相信诸君对于这一点应该不难理解,如果没看过的小伙伴起强烈建议去看一看前面的章节哦!
( 1 . 1 )浮点数之间大小的比较
这里再给大家拓展一个知识,就是我们浮点数大小的比较:
刚刚上面我说我们浮点数的比较一般不用等号 =,那我们要怎样才能进行浮点数之间的比较呢?
如图,咱们这里使用到了一个函数 —— fabs,这个表示的是浮点数的绝对值,还有一个 abs 就是整数的绝对值。咱们浮点数之间的比较用两个数作差的绝对值小于 0.000001(我们可接受的一个误差区间),这样子就可以基本确定我们两个浮点数的大小关系了
(2)结构体的特殊声明
在我们进行结构体声明时,还有一种还有一种特殊声明 —— 不完全声明(匿名结构体类型)
如图,以上就是我们匿名结构体类型的格式,诸君可见,匿名结构体类型就是:声明时省略掉了我们的结构体标签,且我们的匿名结构体声明只能用一次。第二个是我们创建了一个结构体指针变量,可以看到我们这两个结构体除了变量名可以说是一模一样的,那么这时候我想问大家一个问题:
在我们上面创建的两个结构体变量的基础上,ps = &s1 可行么?
答案是否定的,虽然我们两个结构体中的类型和成员一模一样,但是由于是匿名结构体,在系统看来这两个结构体的类型是不相同的,编译器会把上面两个声明当成完全不同的两个类型,所以这种操作是属于非法的
三 . 结构体的自引用
(1)结构体的一般自引用
结构体的自引用,听到这个概念,我相信大多数小伙伴都会想起我们的函数递归之类的,没错,结构体的自引用就跟我们之前学过的函数递归是有着异曲同工之妙的
在讲结构体的自引用之前,我们先来引入一个概念 —— 链表
如图,当我们数据的存储在内存中是混乱的,我们需要将它们串联起来,我们将这样的形式就称为链表,我们结构体的自引用就可以这样模拟实现链表的表达形式,将这份数据放在一个结构体中,再对其自引用去找到下一份数据
我们先来举个例:
如图,我们在结构体中,相当于链表节点的形式,对我们的结构体进行自引用,让其自己找到下一个节点,再自引用,此番循环......这可以么?这是不行的,我们这个结构体内放了一个数据 data,还放了一个自己本身,因此这个结构体所占内存大小我们是无法确定的
正确的结构体自引用格式:
如图,我们换一种思路,换一种方式就可以了,这样子我们存放的是一个数据,这叫数据域,还有存放着下一个节点的地址,这叫指针域,通过这个地址去找到下一个节点,一个整型占 4 个字节,一个地址占 4 / 8 个字节,这样子我们结构体的大小就可以得到确定
这就是我们所谓的结构体的自引用,诸君明白了么?
(1)结构体自引用匿名结构体
这里在深入一个问题:结构体在自引用的过程中,夹杂了 typedef 对匿名结构体类型的重命名,这种情况我们又应该怎样实现呢?大家思考一下我们依旧如下图这样可以吗?
答案是否定的,因为 Node 是对前面的匿名结构体类型重命名产生的,但是我们在匿名结构体内使用就属于提前使用了 Node 类型来创建成员变量,这当然是不行的,所以我们在结构体内部依然要使用完整的结构体类型:
以上就是我们有关结构体自引用的部分知识点
OKK,今天有关结构体的内容就说到这里啦,如果对各位小伙伴有所帮助,不妨点个关注,咱们下期再见吧,与诸君共勉!!!