C语言中结构体指针如何用 -> 取子数据及链表应用示例

在C语言当中,指针箭头"->"看起来是简单的,然而,好多人在学到链表之际,会被它难住。此符号从本质上来说,那是从一个结构体指针里把内部数据取出的快捷途径,要理解它呀,得先弄明白变量、指针、结构体这三层概念方可,不然的话,看链表代码就好似看天书一般。

变量不是房间里会变的东西

有不少人认为变量乃是值会发生变化的数据,只是这样的理解太过浅显,变量的实质是一块固定的内存空间,它具备不变的地址,空间当中存放的内容能够被改变,就如同某间教室这个房间一般,每天前来上课的学生并不相同,然而教室的位置以及大小却是未变的,变量的地址就等同于这间房的门牌号,变量的值恰似房间里的学生,平常我们运用变量名直接对学生进行操作,指针却能够让我们依据门牌号去找到房间。

声明一个整型变量int a,系统会分配一块内存,这块内存是4字节,给它贴上名叫"a"的标签。当你写下a = 5,是将5放进这块空间。但要是写int p = &a,p这个指针变量所存的是a那块空间的地址编号,例如0x7fff1234。此时p就是门牌号的记录本,顺着门牌号找到房间后看到的学生是a,而不是p。

结构体把多个房间打包成别墅

将不同类型数据组合起来的结构体,好似把几个功能各异的房间连接成一栋别墅,例如定义一个含有学号、姓名、成绩的学生结构体,此三个数据于内存之中是连续存放的,在用普通变量struct Student stu时,能够直接借由stu.id去访问学号,然而当使用指针struct Student p指向stu时,便无法再靠着p.id去访问,原因在于p是一个地址,并非结构体自身。

到了这个时候,"->"便发挥其作用了。p->id等同于(p).id,其含义是,首先依据p当中的地址去寻找到那座别墅,接着再获取别墅里的id房间。当编译器对这个符号进行处理之际,会自动去计算结构体成员的偏移量,就像id之处在结构体起始位置偏移0字节,name偏移4字节,成绩偏移20字节。p->id所做的便是取p所指向的地址加上0字节之后的内容。

链表里的箭头是串起别墅的锁链

链表的节点结构体当中,除去存放数据之外,有一个同类型的指针成员是必须存在的,该指针成员用于指向下一个节点,典型的定义呈现为struct Node{ int data; struct Node next; };以上情况里,next被当作一个指针变量,其所存储的内容是下一个节点的地址。要是你存在一个指针p,它指向当前节点,那么p->next所做的便是取出这个节点里所存储的下一个节点的地址,而p = p->next呢,就是将下一个节点的地址赋予p,从而让p指向接下来的那个节点。

对链表进行遍历的过程,便是持续不断地重复这样的操作。起始的时候,p指向头节点,通过访问p->data来获取数据,接着,p = p->next从而移动到下一个节点,一直到p变成NULL才停止。此操作容易出错的原因,在于有不少人将p->next与p弄混淆了。p是当前节点的地址,p->next是下一个节点的地址,它们两者的类型是相同的,然而含义却不一样。

指针的定位器功能决定了链表的灵活性

和数组不一样,链表并不在内存里连续存放,它的节点能够分散于各处。每个节点当中的next指针属于定位器,记录着下一个节点的准确地址。当你写下p = p->next的时候,这就等同于把定位器里的坐标更新成下一个节点的坐标。这种离散存储使得链表能够动态地进行节点的增删操作,相较于数组而言,不需要提前去申请连续的大块内存。

在内存当中,一个链表的节点,有可能位居地址0x1000、0x2050、0x1A80的位置,这些位置彼此并不是相邻的。然而借助next指针串联起来,便能够从一个节点找寻到下一个节点。针对p->next这个操作而言,在底层实际上是读取当前节点地址偏移某个字节之处的值,而这个值就是下一个节点的地址。要是next所存储的是0x2050,那么p->next就等同于0x2050,当赋值给p之后,p便指向了那个崭新的地址。

遍历链表时箭头的使用要区分两种情况

于链表操作里头,存在两种用场会用到箭头。其一乃是去访问当前节点的成员,就好比说p->data、p->next这样的情况,此时p是节点指针来着,箭头是用以取成员的。其二是将next的值赋予p自身,也就是说p = p->next,在这个时候,等号右边的p->next所取出的乃是地址值,等号左边的p是身为存放指针的变量。好多人把这两步给合并成一句话,很容易就忽略掉p->next本身也是属于一个指针类型的值。

写链表代码之际,时常会出现的一个错误乃是,混淆p以及p->next的类型,要是p属于struct Node类型,那么p->next同样是struct Node *类型,此二者能够相互进行赋值,然而倘若你写下p->next = p,那就致使当前节点的next指向自身,从而形成环,在进行遍历时会陷入死循环,要是你写下p = p,那便什么都未曾改变,正确的移动指针唯有p = p->next这一种书写方式。

掌握箭头含义才能看懂链表核心操作

链表所进行的插入操作以及删除操作,均是依赖于对箭头作出正确使用的。举例来说,当于节点prev的后方插入一个全新节点new时,那就需要书写new->next = prev->next; prev->next = new;。其中第一句是要使得新节点的next去指向prev原本的下一个节点,而第二句是要让prev的next去指向新节点。要是这两句书写的顺序颠倒了,那么便会造成后面的节点丢失的情况。要是不借助箭头,直接写成new.next = prev.next,那语法立马就错了,这是因为new是用于指向的指针并非结构体变量呀。

要是在删除节点之际,设若当前的这个节点恰恰就是prev,那么此时就要去删除prev->next所指向的那个节点,其写法应该是prev->next=prev->next->next。在这一句话当中,可以看到两次都用到了箭头,先是通过取prev->next从而获得了那个待删除节点的地址,接着,针对这个已经获取到的地址再去取->next进而得到下下个节点的地址,最终,把这个辛辛苦苦得到的地址赋予给prev->next。而想要弄明白这个链条的关键所在,就是要清晰地分辨出每个箭头究竟是从哪一个指针起始去取的。

瞧见这儿,你理应已然清楚p = p->next并非啥神奇法术,它仅是把当前节点所存的下一个节点的地址提取出来,放置进p这个Pointer变量里头,使得p指向接下来的节点。接着问个事儿:要是链表节点里存在不止一个指针成员,像既有next又有prev,那么于删除节点之际就得分别处置这俩指针,你能够撰写出正确的代码不?欢迎在评论区分享你的写法。

相关推荐
Darling噜啦啦3 天前
列表转树算法深度解析:从 Map 到 Reduce 的两种实现,面试高频考点
数据结构·算法·面试
LDR0064 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
小小工匠4 天前
Redis - 事务机制:能实现 ACID 属性吗
数据结构·redis·性能优化·并发·持久化
Luminous.4 天前
C语言--day30
c语言·开发语言
玖玥拾4 天前
C/C++ 数据结构(七)栈、容器适配器
c语言·数据结构·c++··容器适配器
謓泽4 天前
C语言不是语法,是通往机器的地图。
c语言·开发语言
不会C语言的男孩4 天前
Linux 系统编程 · 第 8 章:进程基础
linux·c语言
Qres8214 天前
算法复键——树状数组
数据结构·算法
2601_951643884 天前
C语言长文整理,关键字和数据类型
c语言·数据类型·关键字·嵌入式开发·格式化输出
m0_547486664 天前
《C#语言程序设计与实践》 全套PPT课件
c语言·c#·c语言程序设计