三 . 链表(列表)与节点(列表项)

发布时间:2026/6/10 8:33:06

三 . 链表(列表)与节点(列表项) 目录C语言的链表FreeRTOS中的链表实现链表节点的插入实验写在最前面的列表和列表项是直接从FreeRTOS源码的注释中的list和list item翻译过来的其实就是我们C语言中的链表和节点。一、C语言的链表链表是C语言中的一种基础的数据结构。在操作系统中用的非常多。举个通俗的例子链表就像一个晾衣架节点就是上面的钩子节点与节点之间首尾相连。钩子不能代表很多东西但是可以挂很多东西节点类似链表的节点不是用来存储大量数据的但是可以挂很多数据。链表分为单向链表和双向链表。单向链表链表的定义以单向链表为例如图该链表有n个节点前一个节点都有一个箭头指向后一个节点首尾相连组成一个圈。节点本身必须包含一个指向下一个节点的指针除此以外还可以携带哪些信息其实节点就是一个自定义的数据结构在这个数据结构里面可以有单个的数据数组指针数据和自定义的结构体数据类型数据...具体代码如下struct node { struct node *next; /* 指向下一个节点 */ char data1; /* 单个的数据 */ unsigned char array[]; /* 数组 */ unsigned long *prt; /* 指针数据 */ struct userstruct data2; /* 自定义结构体类型数据 */ /* ...... */ };通常我们只在节点里包含一个指向下一个节点的指针。我们通过链表存储的数据里内嵌一个节点来实现将存储的数据挂在链表里。具体代码如下下面图形帮助理解双向链表与单向链表的区别是节点中有两个节点指针一个指向前一个节点一个指向后一个节点。如下图辅助理解补充知识点链表与数组有什么区别嵌入式面试常问它们两者确实很像但实际上链表是通过节点将离散的数据链接成一个表通过对节点的插入和删除来实现数据的存取而数组是通过开辟一段连续的内存空间来存储数据。另外数组有起始地址和结束地址而链表是一个圈没有首尾之分。我们为了方便节点的插入和删除人为地引入了一个根节点。这也是我们引入根节点的原因。二、FreeRTOS中的链表实现在学习之前我们要知道FreeRTOS中与链表相关的操作都是在list.c和list.h中实现的。第一次使用我们在include文件夹下新建list.h然后添加到工程FreeRTOS/Source这个组文件在freertos文件夹下新建list.c然后添加到工程FreeRTOS/Source这个组文件中。1. 定义链表节点数据结构链表节点的数据结构在list.h中定义。这里要注意红框标注的分号一定要有。否则报错。下面图形帮助理解这里需要补充的是TickType_t是一个数据类型的重定义还有很多都放在了portmacro.h头文件中首次使用在include文件夹下新建然后添加到工程FreeRTOS/Source这个组文件中。如下图(portmacro.h头文件内容)2. 链表节点初始化链表节点初始化函数在list.c文件中实现。链表节点ListItem_t有5个成员但是初始化的时候只需将pvContainer初始化为空即可表示该节点没有插入到任何链表。3. 定义链表根节点的数据结构链表根节点的数据结构在list.h中定义。链表节点计数器用于表示该链表下有多少个节点根节点除外。链表节点索引指针用于遍历指针。链表最后一个节点字面理解是最后一个节点实际上也是链表的第一个节点所谓头就是尾尾也是头因为链表是一个圈。这个节点我们称之为生产者。【这里注意的是List_t是struct xLIST重定义之后的名字。这是结构体使用typedef数据类型定义的一种方法。区别于独立出来的那种形式但意义是一样的。】链表精简节点结构体在list.h中定义。且放在链表根节点的数据结构前面。综上下面图形帮助理解4. 链表根节点初始化链表根节点初始化函数在list.c中实现。下面图形帮助理解5. 将节点插入到链表的尾部也就是将一个新的节点插入到一个空的链表。那么如何将节点插入到链表的尾部呢结合上图理解假设我们的链表是一个双向链表假设它前面有一个节点它们连接的方式就是上图原来的指向此时我们要将一个新的节点插入到链表的尾部原来的连接也就不复存在“NEW”指代新的节点最后要实现步骤是NEW节点指向的下一个节点就是END步骤1而END的上一个也就指向了NEW步骤4同样原来END的上一个节点(尾部前面的节点)的下一个指向NEW步骤3原来END的下一个节点也就是NEW的下一个节点步骤2。本程序是按照步骤1~4进行编程的(体会用意)。将节点插入到链表的尾部在list.c中实现的。如下图里面的标号与上图指向一致最后要记录一下它属于哪一个链表并将该链表的节点计数器加1。也可以参考下面的图解留下一个问题思考按照其它的赋值顺序行吗步骤123打乱顺序4还放在最后6.将节点按照链表的升序排列插入到链表(放在list.c中)这个函数可以结合上一个函数(vListInserEnd())会更好理解。这里面的序号可以参考上一小节的第一个图。里面的for循环我们可以理解初次将链表的初值赋给pxIterator这个链表节点的结构体指针。根据pxIterator指向的下一个节点指针里的排序值与新插入节点的排序值做比较当判断相等时也就是最后一次执行循环体此时pxIterator也就是要插入那个位置的上一个节点指针。也可以参考下面的图解7. 将节点从链表删除(放在list.c中)最后将要删除的节点的指向链表赋为空表示不指向任一个链表同时原来链表的节点计数器-1并返回当前链表的节点数。也可以参考下面的图解8.学习节点带参宏的小函数有哪些都是怎么实现的/* 初始化节点的拥有者 */ #define listSET_LIST_ITEM_OWNER( pxListItem, pxOwner)\ ( ( pxListItem ) -pvOwner ( void * ) ( pxOwner ) ) /* 读取节点的拥有者 */ #define listGET_LIST_ITEM_OWNER( pxListItem )\ ( ( pxListItem ) - pvOwner ) /* 初始化节点排序辅助值 */ #define listSET_LIST_ITEM_VALUE( pxListItem, xValue )\ ( ( pxListItem ) - xItemValue ( xValue )) /* 获取节点排序辅助值 */ #define listGET_LIST_ITEM_VALUE( pxListItem )\ ( (pxListItem ) - xItemValue ) /* 获取链表根节点的节点计数器的值 */ #define listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxList )\ ( ( ( pxList ) - xListEnd ).pxNext - xItemValue ) /* 获取链表的入口节点 */ #define listGET_HEAD_ENTRY( pxList )\ ( ( ( pxList ) - xListEnd ).pxNext ) /* 获取节点的下一个节点 */ #define listGET_NEXT( pxListItem )\ ( ( pxListItem ) - pxNext ) /* 获取链表的最后一个节点 */ #define listGET_END_MARKER( pxList )\ ( ( ListItem_t const * ) ( ( ( pxList ) - xListEnd ) ) ) /* 判断链表是否为空 */ #define listLIST_IS_EMPTY( pxList )\ ( ( BaseType_t ) ( ( pxList ) - uxNumberOfItems ( UBaseType_t ) 0 ) ) /* 获取链表的节点数 */ #define listCURRENT_LIST_LENGTH( pxList )\ ( ( pxList ) - uxNumberOfItems ) /* 获取链表第一个节点的OWNER即TCB */ #define ListGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList ) { List_t * const pxConstList ( pxList ); /* 节点索引指向链表第一个节点 */ \ ( pxConstList ) - pxIndex ( pxConstList ) - pxIndex - pxNext ; /* 这个操作有啥用 */ \ if( ( void *) ( pxConstList ) - pxIndex ( void * ) ( ( pxConstList ) - xListEnd ) ) \ { ( pxConstList ) - pxIndex ( pxConstList ) - pxIndex - pxNext; } /* 获取节点的OWNER,即TCB */ \ ( pxTCB ) ( pxConstList ) - pxIndex - pvOwner; }三、链表节点的插入实验参考源代码

相关新闻