FreeRTOS学习(3)-链表和节点程序分析

前文提要
FreeRTOS学习(1)—为什么使用RTOS
FreeRTOS学习(2)-链表和节点之结构体分析

1、RTOS代码中的链表程序–五大程序

(1)链表节点初始化
(2)链表(根节点)初始化
(3)节点插入链表的尾部
(4)节点按照升序排列插入链表
(5)节点从链表删除
在这里插入图片描述

注:我们可以发现,这些函数的参数指针都加了const,这里我认为是一种保护手段。也就是让这些指针在函数内部是只读的,不允许在函数内部被修改。也就意味着,这些指针指向的地址是常量(链表和节点的地址)不可被更改,但是地址本身的数据(链表和节点的数据)是可以改的。

2、链表节点初始化

(1)源代码

在这里插入图片描述

(2)程序解释

    初始化链表节点的主要步骤是将节点的容器指针(pvContainer)设置为NULL,表示该节点还没有被插入到任何链表中。

3、链表(根节点)初始化

(1)源代码

在这里插入图片描述
注:这里有一个强制指针类型转换的操作,我们在下面的节点插入进行说明。

(2)链表结构图示:如图一个链表实际上有五个参数,

在这里插入图片描述

①两个是链表层面的:uxNumberOfItems(链表节点计数器)、pxIndex(链表节点索引指针)
②三个是根节点的:xItemValue(辅助排序)、
pxNext(下一节点指针)、* pxPrevious(上一节点指针)

(3)初始化过程红色部分(重点)

①节点计数器uxNumberOfItems设置为0:根节点不算节点数
②*pxIndex(链表节点索引指针)指向根节点
③节点的辅助排序的值设置为最大(这里我理解的是这个值就是相当于给节点打标号,根节点最大使得根节点总是在最后(开头)
④根节点的上一个和下一个节点都指向自身
在这里插入图片描述

4、节点插入链表的尾部

(1)源代码

在这里插入图片描述

(2)节点插入链表尾部过程

问题1:ListItem_t * const pxIndex = pxList->pxIndex;啥意思?
    笔者在可考虑这个问题的时候,第一反应就是说把链表的索引指针赋给pxIndex。但是同时也有一个疑惑,作为链表的索引指针,这个东西是会随着你索引进行移动的吗?如果移动的话这就不是插入尾部了吧。后面在重新看链表初始化的时候发现多想了。pxList->pxIndex在链表初始化的时候被赋值指向整个链表的尾部,在其他地方没有再赋值过。
    换句话说,这是一个固定指向尾部节点的指针,它提供了一个快速访问尾部节点的方式。平常在使用的时候,我们会把这个指针的值赋给新指针使用,它本身不改变。例如我们要通过这个尾部指针遍历整个链表可以这么写:

ListItem_t *current = pxList->pxIndex;
while (current != NULL) 
{
    // 处理当前节点
    current = current->pxNext; // 移动到下一个节点
}

我在想这个东西pxIndex为啥不直接叫pxhead或者pxend更能体现意思。

图示:插入尾部前

在这里插入图片描述

图示:插入尾部后,红色是变动

在这里插入图片描述

    这代码几句有点绕,这样想:抓住没变动之前和之后的,看看怎么赋值。红框就是没插入之前的。而且pxIndex位置在根节点,这样理解起来容易。也可以看图有几根红线说明要有几句话进行变动。
在这里插入图片描述

5、节点按照升序排列插入链表

(1)源代码1–找插入位置

在这里插入图片描述

*在这里对强制指针类型转换进行说明。
    pxIterator = ( ListItem_t * ) &( pxList->xListEnd );我们可以看到ListItem_t *pxIterator;是ListItem_t(节点)类型的指针。MiniListItem_t xListEnd;; 是链表的终结节点(类型为 MiniListItem_t)指针。

问题1:为什么要做这样一个强制转化?(MiniListItem_t–ListItem_t)

    是为了使得链表操作统一处理。这种转换利用了 C 语言中的强制类型转换来实现不同结构体类型间的指针兼容性。实现了简化边界条件处理。这在实现双向链表时特别有用,尤其是当终结节点(mini节点)与普通节点需要统一处理时。

问题2:这两个结构体有不同的成员,强制转化后是啥样子?

    强制类型转换使得我们以通过 ListItem_t * 指针来访问 MiniListItem_t 的成员,但只会访问它们共有的部分(而访问MiniListItem_t 结构体中没有的成员 pvOwner 和 pvContainer 会导致未定义行为)。特别是在需要遍历链表或进行插入、删除等操作时。这种类型转换利用了结构体内存布局的一致性,使得转换后的指针可以正确地访问共有的成员

问题3:如果我不进行强制转换,有什么问题吗?

①代码复杂性增加
    没有强制转换,处理 MiniListItem_t 和 ListItem_t 时必须分别编写不同的代码。这样做会增加代码的复杂性和维护难度。例如,在链表的插入操作中,需要分别处理普通节点和终结节点。例如:在遍历链表时,如果不进行强制转换,必须在遍历过程中区分普通节点和终结节点
②总结
    通过将 MiniListItem_t * 转换为 ListItem_t *,可以简化链表操作代码,减少类型检查和特殊处理的复杂性,确保操作的一致性。如果不进行这种转换,代码复杂性和维护难度会增加,操作中容易引入错误。因此,在实现链表时,进行这种强制转换是一个常见且有效的做法。

(2)源代码2–插入

在这里插入图片描述

(3)图示–上面的四条语句对应图中的四条红线。可以逐一分析。

在这里插入图片描述
在这里插入图片描述

6、节点从链表删除

(1)源代码

在这里插入图片描述

问题4:这里的强制类型转化作用?

( List_t * ) 是一个强制类型转换操作,
    将 pxItemToRemove->pvContainer(void * 类型)转换为 List_t * 类型这种转换告诉编译器将 pvContainer 视为指向 List_t 结构体的指针。
为啥使用:
    因为 pvContainer 是一个 void * 类型的指针,它没有类型信息,所以在使用时需要将其转换为具体的类型(在这里是 List_t *),以便可以安全地访问 List_t 结构体的成员。这里涉及无类型指针。

(2)示意图:同样的,几个语句对应图中红色的更改部分
删除前:
在这里插入图片描述

删除后:4个更改地方
在这里插入图片描述

7、一些宏定义,便于快速操作(list.h)
在这里插入图片描述

注:这个表用于速查,有些函数不能望文生义
①例如listGET_OWNER_OF_HEAD_ENTRY( pxList ):这个函数像是头部节点的拥有者,实际上他是根节点的下一个节点作为头部节点,查看拥有者。

个人学习文档,有问题欢迎大家评论交流,如果感到有用的话点个赞吧。ヽ(。◕‿◕。)ノ゚

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值