目录
在FreeRTOS中存在着大量的基础数据结构列表和列表项的操作,要想读懂FreeRTOS的源码或者从0到1开始实现FreeRTOS,就必须弄懂列表和列表项的操作,其实也没那么难。
列表和列表项是直接从FreeRTOS源码的注释中的list 和 list item翻译过来的,其实就是对应我们C语言当中的链表和节点,在后续的讲解中,我们说的链表就是列表,节点就是列表项。
1. C语言链表
链表作为C语言中一种基础的数据结构,在平时写程序的时候用的并不多,但在操作系统里面使用的非常多。链表就好比一个圆形的晾衣架,晾衣架上面有很多钩子,钩子首尾相连。链表也是,链表由节点组成,节点与节点之间首尾相连。
晾衣架的钩子本身不能代表很多东西,但是钩子本身却可以挂很多东西。同样,链表也类似,链表的节点本身不能存储太多东西,或者说链表的节点本来就不是用来存储大量数据的,但是节点跟晾衣架的钩子一样,可以挂很多数据。
链表分为单向链表和双向链表,单向链表很少用,使用最多的还是双向链表。
1.1 单向链表
单向链表示意图。该链表中共有n个节点,前一个节点都有一个箭头指向后一个节点,首尾相连,组成一个圈。
节点本身必须包含一个节点指针,用于指向后一个节点,除了这个节点指针是必须有的之外,节点本身还可以携带一些私有信息,怎么携带?
节点都是一个自定义类型的数据结构,在这个数据结构里面可以有单个的数据、数组、指针数据和自定义的结构体数据类型等等信息。
struct node {
struct node *pNext; /* 指向链表的下一个节点 */
char cData; /* 单个数据 */
unsigned char array[]; /* 数组数据 */
unsigned long * ptr; /* 指针数据 */
struct usrStruct xData; /* 自定义结构体数据 */
/* ... ... */
};
除了struct node *next 这个节点指针之外,剩下的成员都可以理解为节点携带的数据,但是这种方法很少用。通常的做法是节点里面只包含一个用于指向下一个节点的指针。要通过链表存储的数据内嵌一个节点即可,这些要存储的数据通过这个内嵌的节点即可挂接到链表中,就好像晾衣架的钩子一样,把衣服挂接到晾衣架中。
struct node {
struct node *pNext; /* 指向链表的下一个节点 */
};
1.2 双向链表
双向链表与单向链表的区别就是节点中有两个节点指针,分别指向前后两个节点,其它完全一样。有关双向链表的文字描述参考单向链表小节即可。
1.3 链表的操作
链表常规的操作就是节点的插入和删除,为了顺利的插入,通常一条链表我们会人为地规定一个根节点,这个根节点称为生产者。通常根节点还会有一个节点计数器,用于统计整条链表的节点个数。
有关链表节点的删除和操作的代码讲解这里先略过,具体的可参考本章接下来的“FreeRTO中链表的实现”小节,在这个小节里面会有非常详细的讲解,这里我们先建立概念为主。
1.4 链表与数组的对比
两者的示意图
链表是通过节点把离散的数据链接成一个表,通过对节点的插入和删除操作从而实现对数据的存取。而数组是通过开辟一段连续的内存来存储数据,这是数组和链表最大的区别。
数组的每个成员对应链表的节点,成员和节点的数据类型可以是标准的C类型或者是用户自定义的结构体。
数组有起始地址和结束地址,而链表是一个圈,没有头和尾之分,但是为了方便节点的插入和删除操作会人为的规定一个根节点。