FreeRTOS数据结构——列表与列表项

2023-05-16

在FreeRTOS中存在着大量的基础数据结构列表和列表项的操作,要想读懂FreeRTOS的源代码,就必须弄懂列表和列表项的操作。

一、C语言链表简介

链表就好比一个圆形的晾衣架,晾衣架上有很多钩子,钩子首尾相连。链表也是,链表由节点组成,节点与节点之间首尾相连。晾衣架的钩子本身不能代表很多东西,但是钩子本身可以挂很多东西。同样,链表也类似,链表的节点本身不可能存储太多东西,但是节点跟晾衣架的钩子一样,可以挂很多数据。
在这里插入图片描述
链表分为单向链表和双向链表。

1、单向链表

单向链表如下图所示,该链表中有n个节点,前一个节点都有一个箭头指向后一个节点,首尾相连,组成一个圈。
在这里插入图片描述
节点本身必须包含一个节点指针,用于指向后一个节点,除了节点指针之外,节点本身还可以携带一些私有信息。通常做法是节点里面只包含一个用于指向下一个节点的指针,要通过链表存储的数据内嵌一个节点即可,这些要存储的数据通过这个内嵌的节点即可挂接到链表中。
在这里插入图片描述

2、双向链表

双向链表与单向链表的区别就是节点中有两个节点指针,分别指向前后两个节点,其他完全一样。
在这里插入图片描述

3、链表与数组对比

在这里插入图片描述

链表数组
通过节点把离散的数据连接成一个表 ,通过对节点的插入和删除实现对数据的存取通过开辟一段连续的内存存储数据
是一个圈,没有首尾之分,但会规定一个根节点有起始地址和结束地址

数组的每个成员对应链表中的节点。

二、FreeRTOS中链表的实现

FreeRTOS中与链表相关的操作均在list.h和list.c这两个文件中实现,list.h 第一次使用需要在 include 文件夹下面新建然后添加到工程freertos/source 这个组文件, list.c 第一次使用需要在 freertos 文件夹下面新建然后添加到工程 freertos/source 这个组文件。

1、实现链表节点

1.1 定义链表节点数据结构

链表节点的数据结构在list.h中定义,如下图:
在这里插入图片描述
(1)辅助值 用于帮助节点做顺序排列。该辅助值的数据类型TickType_t, 在 FreeRTOS 中,凡是涉及到数据类型的地方,FreeRTOS 都会将标准的 C 数据类型用 typedef 重新取一个类型名,这些经过重定义的数据类型放在 portmacro.h(portmacro.h 第一次使用需要在 include 文件夹下面新建然后添加到工程 freertos/source 这个组文件)这个头文件。TickType_t 具 体 表 示 16 位 还 是 32 位 , 由configUSE_16_BIT_TICKS 这个宏决定, 当该宏定义为 1 时, TickType_t 为 16 位,否则为32 位。该宏在FreeRTOSConfig.h(FreeRTOSConfig.h 第一次使用需要在 include 文件夹下面新建然后添加到工程 freertos/source 这个组文件) 中默认定义为 0。
(4)指向该节点的拥有者,即该节点内嵌在哪个数据结构中。
(5)指向该节点所在链表,通常指向链表的根节点

1.2 链表节点初始化

链表节点初始化在list.c中完成。
在这里插入图片描述
链表节点ListItem_t总共有五个成员,初始化时只需将pvContainer初始化为空即可,表示该节点还没有插入任何节点。

2 实现链表根节点

2.1 定义链表根节点数据结构

列表根节点的数据结构在list.h中实现。

typedef struct xLIST
{
	UBaseType_t uxNumberOfItems;    /* 链表节点计数器   (1)*/
	ListItem_t *  pxIndex;			/* 链表节点索引指针 (2)*/
	MiniListItem_t xListEnd;		/* 链表最后一个节点 (3)*/
} List_t;

(1):用于记录该链表下节点个数,除根节点外
(2):用于遍历节点
(3):链表最后一个节点。链表首尾相连,即链表的最后一个节点也是链表的第一个节点,我们称之为生产者。该生产者的数据类型定义在list.h中,如下:

struct xMINI_LIST_ITEM
{
 TickType_t xItemValue; /* 辅助值,用于帮助节点做升序排列 */
 struct xLIST_ITEM * pxNext; /* 指向链表下一个节点 */
 struct xLIST_ITEM * pxPrevious; /* 指向链表前一个节点 */
};
typedef struct xMINI_LIST_ITEM MiniListItem_t; /* 精简节点数据类型重定义 */

2.2 链表根节点初始化

链表根节点初始化在list.c中实现。

void vListInitialise( List_t * const pxList )
{
 /* 将链表索引指针指向最后一个节点 */(1)
 pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd );

 /* 将链表最后一个节点的辅助排序的值设置为最大,确保该节点就是链表的最后节点 */(2)
 pxList->xListEnd.xItemValue = portMAX_DELAY;

 /* 将最后一个节点的 pxNext 和 pxPrevious 指针均指向节点自身,表示链表为空 */(3)
 pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );
 pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );

 /* 初始化链表节点计数器的值为 0,表示链表为空 */(4)
 pxList->uxNumberOfItems = ( UBaseType_t ) 0U;
}

2.3 将节点插入到链表的尾部

链表根节点初始化在list.c中实现。将节点插入到链表的尾部(也可理解为头部)就是讲一个新节点插入到一个空链表中。

void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
{
 ListItem_t * const pxIndex = pxList->pxIndex;

 pxNewListItem->pxNext = pxIndex;1)
 pxNewListItem->pxPrevious = pxIndex->pxPrevious;2)
 pxIndex->pxPrevious->pxNext = pxNewListItem;3)
 pxIndex->pxPrevious = pxNewListItem;4/* 记住该节点所在的链表 */
 pxNewListItem->pvContainer = ( void * ) pxList;5/* 链表节点计数器++ */
 ( pxList->uxNumberOfItems )++;6}

2.4 将节点按照升序排列插入到链表

将节点按照升序排列插入到链表,如果两个节点的值相同,则新节点在旧节点后面插入。

void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
{
 ListItem_t *pxIterator;

 /* 获取节点的排序辅助值 */
 const TickType_t xValueOfInsertion = pxNewListItem->xItemValue; (1)

 /* 寻找节点要插入的位置 */ (2)
 if ( xValueOfInsertion == portMAX_DELAY )
 {
 pxIterator = pxList->xListEnd.pxPrevious;
 }
 else
 {
 for ( pxIterator = ( ListItem_t * ) &( pxList->xListEnd );
 pxIterator->pxNext->xItemValue <= xValueOfInsertion;
 pxIterator = pxIterator->pxNext )
 {
 /* 没有事情可做,不断迭代只为了找到节点要插入的位置 */
 }
 }
 /* 根据升序排列,将节点插入 */ (3)
 pxNewListItem->pxNext = pxIterator->pxNext; 
 pxNewListItem->pxNext->pxPrevious = pxNewListItem; 
 pxNewListItem->pxPrevious = pxIterator; 
 pxIterator->pxNext = pxNewListItem; 
 /* 记住该节点所在的链表 */
 pxNewListItem->pvContainer = ( void * ) pxList; 

 /* 链表节点计数器++ */
 ( pxList->uxNumberOfItems )++; 
}

2.4 将节点从链表中删除

假设将一个有三个节点的链表中的中间节点删除。

UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
 /* 获取节点所在的链表 */
 List_t * const pxList = ( List_t * ) pxItemToRemove->pvContainer;
 /* 将指定的节点从链表删除*/
 pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious; ①
 pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext; 

 /*调整链表的节点索引指针 */
 if ( pxList->pxIndex == pxItemToRemove )
 {
 pxList->pxIndex = pxItemToRemove->pxPrevious;
 }
 /* 初始化该节点所在的链表为空,表示节点还没有插入任何链表 */
 pxItemToRemove->pvContainer = NULL; 
 /* 链表节点计数器-- */
 ( pxList->uxNumberOfItems )--; 
 /* 返回链表中剩余节点的个数 */
 return pxList->uxNumberOfItems;
}

三、链表节点插入试验

我们新建了一个根节点和三个普通节点,然后将这三个普通节点按照节点的排序辅助值做升序排列插入到链表中。

 /*
 *************************************************************************
 * 包含的头文件
 *************************************************************************
 */
 #include "list.h"
 /*
  *************************************************************************
 * 全局变量
 *************************************************************************
 */
 /* 定义链表根节点 */
 struct xLIST List_Test; (1)
 /* 定义3个普通节点 */
 struct xLIST_ITEM List_Item1; (2)
 struct xLIST_ITEM List_Item2;
 struct xLIST_ITEM List_Item3;
 /*
 ************************************************************************
 * main 函数
 ************************************************************************
 int main(void)
 {
 /* 链表根节点初始化 */
 vListInitialise( &List_Test ); (3)
  /* 节点 1 初始化 */
 vListInitialiseItem( &List_Item1 ); (4)
 List_Item1.xItemValue = 1;
 /* 节点 2 初始化 */
 vListInitialiseItem( &List_Item2 );
 List_Item2.xItemValue = 2;
 /* 节点 3 初始化 */
 vListInitialiseItem( &List_Item3 );
 List_Item3.xItemValue = 3;
 /* 将节点插入链表,按照升序排列 */ (5)
 vListInsert( &List_Test, &List_Item2 );
 vListInsert( &List_Test, &List_Item1 );
 vListInsert( &List_Test, &List_Item3 );

 while(1)
 {
 }
 }

程序编译之后,点击调试按钮,全速运行,然后把List_Test、List_Item1、List_Item2、List_Item3这四个全局变量添加到watch窗口进行观察。
在这里插入图片描述
参考:[野火®]《FreeRTOS 内核实现与应用开发实战—基于STM32》

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

FreeRTOS数据结构——列表与列表项 的相关文章

随机推荐

  • 【CMake】CMakeLists.txt的超傻瓜手把手教程(附实例源码)

    新手写CMakeLists txt简直就是实力劝退 xff0c 各种命令让很多人头大 xff0c 如何写一个最基础的CMakeLists txt呢 xff1f 本文从一个实例出发 xff0c 教你编写的基本流程 CMakeLists txt
  • 【Gradle】Groovy的语法详解(下篇)

    上文介绍了Groovy的一般省略规则 xff0c 以及字符串 列表 Map的使用方式 xff0c 然而在Groovy中 xff0c 闭包是其中的一大杀器 xff0c 可以对代码的结构和逻辑带来的优化显而易见 本博文主要对Groovy闭包的使
  • 【OpenCV】OpenCV图像/视频的读取与写入(C++版)

    使用C 43 43 开发图像处理算法时 xff0c 最基础的就是利用OpenCV完成图像文件的输入 输出以及自动内存管理 重点 所以 xff0c 只要需要掌握一些简单的OpenCV的操作即可 本博文就对这些基础内容进行讲解 图像操作 图像读
  • 【OpenCV】OpenCV常用函数(C++版)

    俗话说 xff1a 好记性不如烂笔头 在使用OpenCV的过程中 xff0c 时常会用到很多函数 xff0c 而且往往可能会一时记不起这个函数的具体参数怎么设置 xff0c 故在此将常用函数做一汇总 图像缩放与放大 对图像的各项操作中 xf
  • 【Python】python曲线拟合

    python作为一款可以简单方便地进行科学计算的语言 xff0c 进行曲线拟合自然是必备的功能之一了 本文就如何进行曲线拟合进行讲解 本文需要进行拟合的数据为 xff1a x 61 np arange 1 31 1 y 61 np arra
  • 【C++】NULL和nullptr的关联与差别

    在写代码的过程中 xff0c 有时候需要将指针赋值为空指针 xff0c 以防止野指针 在C中 xff0c 都是使用NULL来实现的 xff1b 在C 43 43 中 xff0c 除了NULL之外 xff0c 还提供了nullptr来进行定义
  • 【C++】C++11可变参数模板(函数模板、类模板)

    在C 43 43 11之前 xff0c 类模板和函数模板只能含有固定数量的模板参数 C 43 43 11增强了模板功能 xff0c 允许模板定义中包含0到任意个模板参数 xff0c 这就是可变参数模板 可变参数模板的加入使得C 43 43
  • 【C++】C++11统一初始化(initializer_list<T>源码分析)

    C 43 43 11之前的初始化语法很乱 xff0c 有四种初始化方式 xff0c 而且每种之前甚至不能相互转换 让人有种剪不断 xff0c 理还乱的感觉 因此 xff0c C 43 43 11添加了统一初始化的方式 xff0c 本文将对统
  • 【C++】右值引用、移动语义、完美转发(上篇)

    在C 43 43 11 xff0c 引入了右值引用的概念 xff0c 在此基础上的移动语义在STL容器中使用非常广泛 简单来说 xff0c move语义使得你可以用廉价的move赋值替代昂贵的copy赋值 xff0c 完美转发使得可以将传来
  • 【C++】右值引用、移动语义、完美转发(下篇)

    上篇中 xff0c 主要讲解了右值引用和移动语义的具体定义和用法 在C 43 43 11中几乎所有的容器都实现了移动语义 xff0c 以方便性能优化 本文以C 43 43 11容器中的insert方法为例 xff0c 详细讲解在容器中移动语
  • AI==喜茶??

    2017年7月10日 xff0c 上海 xff0c 雨 刚从某CV方向的公司下班 xff0c 骑着小黄车朝着浦东某郊区租了一个月的床位行驶着 xff0c 雨打在脸上 xff0c 有点生疼 我不禁在思考 xff0c 这一切到底为了什么 xff
  • 【C++】unique_ptr独占型智能指针详解

    指针是C C 43 43 区别于其他语言的最强大的语法特性 xff0c 借助指针 xff0c C C 43 43 可以直接操纵内存内容 但是 xff0c 指针的引入也带来了一些使用上的困难 xff0c 这要求程序员自己必须手动地对分配申请的
  • 【C++】shared_ptr共享型智能指针详解

    指针是C C 43 43 区别于其他语言的最强大的语法特性 xff0c 借助指针 xff0c C C 43 43 可以直接操纵内存内容 但是 xff0c 指针的引入也带来了一些使用上的困难 xff0c 这要求程序员自己必须手动地对分配申请的
  • 【C++】weak_ptr弱引用智能指针详解

    weak ptr这个指针天生一副小弟的模样 xff0c 也是在C 43 43 11的时候引入的标准库 xff0c 它的出现完全是为了弥补它老大shared ptr天生有缺陷的问题 相比于上一代的智能指针auto ptr来说 xff0c 新进
  • 【C++】模板声明与定义不分离

    一般在写C 43 43 相关代码的时候 xff0c 我们总习惯于将类声明和类实现进行分离 也就是说 xff0c 类的声明一般写在 h文件中 xff0c 而它的实现一般写在 cpp文件中 但是 xff0c 在模板类中 xff0c 这个习惯却要
  • 【C++】空指针调用成员函数及访问成员变量

    最近在review代码的时候发现 xff0c 使用了空指针调用成员函数 xff0c 并且成员函数内部有使用到成员变量 xff0c 居然没有出错 很是奇怪 xff0c 就用一篇博客把关于空指针调用成员函数相关的内容总结起来 空指针调用成员函数
  • 【C++】两个类的相互引用

    有时候在设计数据结构的时候 xff0c 可能会遇到两个类需要相互引用的情形 比如类A有类型为B的成员 xff0c 而类B又有类型为A的成员 那么这种情形下 xff0c 两个类的设计上需要注意什么呢 xff1f 同一文件 尝试方案 将A和B的
  • 【滤波】离散贝叶斯滤波器

    本文参考自rlabbe Kalman and Bayesian Filters in Python的第2章节02 Discrete Bayes xff08 离散贝叶斯 xff09 span class token operator span
  • 用iPad编写C/C++代码(计算机考研党也能用iPad写算法题)

    下载iSH软件 1 在AppStore商店中下载名叫iSH Shell的软件 PS xff1a iSH是一个使用用户模式x86模拟器在iOS设备上获得本地运行的Linux Shell环境的项目 2 安装后点开iSH xff0c 初步了解iS
  • FreeRTOS数据结构——列表与列表项

    在FreeRTOS中存在着大量的基础数据结构列表和列表项的操作 xff0c 要想读懂FreeRTOS的源代码 xff0c 就必须弄懂列表和列表项的操作 一 C语言链表简介 链表就好比一个圆形的晾衣架 xff0c 晾衣架上有很多钩子 xff0