1898_野火FreeRTOS教程阅读笔记_链表操作

1898_野火FreeRTOS教程阅读笔记_链表操作

全部学习汇总: g_FreeRTOS: FreeRTOS学习笔记 (gitee.com)

新的节点的插入,影响到的是链表中最后一个元素的后继以及当前被插入元素的前驱、后继以及归属属性。具体的操作效果为:新的节点更新自己的前驱和后继,而对等的关联信息则是当前pxIndex所指向的前驱和链表的尾结点。而链表的尾结点在初始化的时候,pxIndex存储的其实是指向链表尾结点Item的指针。因此,这里的这个赋值更新,其实是实现了让这个新的节点指向了链表的尾节点。

这第一次用到了xItemValue的元素,其实这个元素的数值算是一个元素在链表上的位置的权重信息。如果这个数值很大,意味着需要插入到链表的最后。从效果上来讲,直接调用插入到End的接口也是可行的。这里相当于把对应逻辑重新写了一遍,但是从函数调用的资源消耗角度来说,这种写法应该效率高。插入的位置点寻找原则是从List的开头向后寻找,插入到数值小于等于自己的元素最后面。而最后的链表归属以及链表中元素个数的处理,与插入End其实是一回事儿。

对于这样的数据结构设计,链表节点的删除实现相当容易:

  • 前驱之后继为吾之后继
  • 后继之前驱为吾之前驱
  • 解脱list归属关系
  • 统计节点数目需要减1

代码中还增加了一个pxIndex的处理,这个也是围绕现在的数据结构所作的特殊处理。主要是考虑到移除的节点是List最后一个节点的情况。

关于链表的测试部分可以有很多,这个教程中用到的不是很多。针对这个仿真工具我了解不多,因此除了Memory的查看之外我额外增加了辅助显示的测试代码。

上面是我修改之后的测试代码,增加了一个辅助查看信息的printf。在调试工具中,我觉得printf可能是使用最顺手的一个工具。这让我感觉到软件是活的,计算机是活的,它们是可以与我们进行交流的。

邯郸学步,我也获得了这个存储查看的信息。

进行信息打印,得出来的信息其实也很容易验证软件的功能是否符合我的期待。工作这么久,我做软件调试的时候可能还是过重依赖于高端的调试器。现在体验一下这种软件的仿真功能,感觉设计还真是不错!

我增加了一点链表操作的测试,主要是看了一下节点的删除功能。同时,也看一下链表的尾节点信息。具体的测试代码如下:

int main(void)
{
    struct xLIST_ITEM *p_item;
    UBaseType_t list_item_number;    printf("start simulation...\n");    vListInitialise(&List_Test);    vListInitialiseItem(&List_Item1);
    vListInitialiseItem(&List_Item2);
    vListInitialiseItem(&List_Item3);    List_Item1.xItemValue = 1;
    List_Item2.xItemValue = 2;
    List_Item3.xItemValue = 3;    vListInsert(&List_Test, &List_Item2);
    vListInsert(&List_Test, &List_Item3);
    vListInsert(&List_Test, &List_Item1);    printf("address of List_Test: 0x%p\n", &List_Test);
    printf("address of List_Item1: 0x%p\n", &List_Item1);
    printf("address of List_Item2: 0x%p\n", &List_Item2);
    printf("address of List_Item3: 0x%p\n", &List_Item3);
    printf("number of items: %d\n", List_Test.uxNumberOfItems);
    p_item = List_Test.pxIndex->pxNext;
    printf("item 1: 0x%p\n", p_item);
    p_item = p_item->pxNext;
    printf("item 2: 0x%p\n", p_item);
    p_item = p_item->pxNext;
    printf("item 3: 0x%p\n", p_item);
    p_item = p_item->pxNext;
    printf("item end: 0x%p\n", p_item);    printf("\ntry to remove List_Item2...\n");
    list_item_number = uxListRemove(&List_Item2);
    printf("item number of List_Test: %d\n", list_item_number);    printf("address of List_Test: 0x%p\n", &List_Test);
    printf("address of List_Item1: 0x%p\n", &List_Item1);
    printf("address of List_Item2: 0x%p\n", &List_Item2);
    printf("address of List_Item3: 0x%p\n", &List_Item3);
    printf("number of items: %d\n", List_Test.uxNumberOfItems);
    p_item = List_Test.pxIndex->pxNext;
    printf("item 1: 0x%p\n", p_item);
    p_item = p_item->pxNext;
    printf("item 2: 0x%p\n", p_item);
    p_item = p_item->pxNext;
    printf("item 3: 0x%p\n", p_item);    for(;;)
    {
        /* no code */
    }    return 0;
}

运行仿真,对应的结果如下:

删除之后,继续往后接着就碰到了尾结点。这个跟预期的效果也是一样的。

进一步进行测试的扩展,看一下双向链表是否是一个环形逻辑结构。修改代码如下:

int main(void)
{
    struct xLIST_ITEM *p_item;
    UBaseType_t list_item_number;    printf("start simulation...\n");    vListInitialise(&List_Test);    vListInitialiseItem(&List_Item1);
    vListInitialiseItem(&List_Item2);
    vListInitialiseItem(&List_Item3);    List_Item1.xItemValue = 1;
    List_Item2.xItemValue = 2;
    List_Item3.xItemValue = 3;    vListInsert(&List_Test, &List_Item2);
    vListInsert(&List_Test, &List_Item3);
    vListInsert(&List_Test, &List_Item1);    printf("address of List_Test: 0x%p\n", &List_Test);
    printf("address of List_Item1: 0x%p\n", &List_Item1);
    printf("address of List_Item2: 0x%p\n", &List_Item2);
    printf("address of List_Item3: 0x%p\n", &List_Item3);
    printf("number of items: %d\n", List_Test.uxNumberOfItems);
    p_item = List_Test.pxIndex->pxNext;
    printf("item 1: 0x%p\n", p_item);
    p_item = p_item->pxNext;
    printf("item 2: 0x%p\n", p_item);
    p_item = p_item->pxNext;
    printf("item 3: 0x%p\n", p_item);
    p_item = p_item->pxNext;
    printf("item end: 0x%p\n", p_item);    printf("\ntry to remove List_Item2...\n");
    list_item_number = uxListRemove(&List_Item2);
    printf("item number of List_Test: %d\n", list_item_number);    printf("address of List_Test: 0x%p\n", &List_Test);
    printf("address of List_Item1: 0x%p\n", &List_Item1);
    printf("address of List_Item2: 0x%p\n", &List_Item2);
    printf("address of List_Item3: 0x%p\n", &List_Item3);
    printf("number of items: %d\n", List_Test.uxNumberOfItems);
    p_item = List_Test.pxIndex->pxNext;
    printf("item 1: 0x%p\n", p_item);
    p_item = p_item->pxNext;
    printf("item 2: 0x%p\n", p_item);
    p_item = p_item->pxNext;
    printf("item 3: 0x%p\n", p_item);
    p_item = p_item->pxNext;
    printf("item 4: 0x%p\n", p_item);
    p_item = p_item->pxNext;
    printf("item 5: 0x%p\n", p_item);
    p_item = p_item->pxNext;
    printf("item 6: 0x%p\n", p_item);
    p_item = p_item->pxNext;
    printf("item 7: 0x%p\n", p_item);    for(;;)
    {
        /* no code */
    }    return 0;
}

仿真运行效果:

这个结果也是很符合预期的。

看完这部分,其实本身链表相关的技能或者知识没有什么变化。但是从仿真工具的使用上的确是收获不少。工具用着比较顺手,关于前面的代码分析不妨再进行一部分测试。

第一部分是关于节点插入函数的,我修改成了如下的逻辑:

/* 将节点按照升序排列插入到链表 */
void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
{
	ListItem_t *pxIterator;	/* 获取节点的排序辅助值 */
	const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;	/* 寻找节点要插入的位置 */
	if( xValueOfInsertion == portMAX_DELAY )
	{
		pxIterator = pxList->xListEnd.pxPrevious;
        vListInsertEnd(pxList, pxNewListItem);
        return;
	}
	else
	{
		for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd );
		     pxIterator->pxNext->xItemValue <= xValueOfInsertion; 
			 pxIterator = pxIterator->pxNext )
		{
			/* 没有事情可做,不断迭代只为了找到节点要插入的位置 */			
		}
	}	pxNewListItem->pxNext = pxIterator->pxNext;
	pxNewListItem->pxNext->pxPrevious = pxNewListItem;
	pxNewListItem->pxPrevious = pxIterator;
	pxIterator->pxNext = pxNewListItem;	/* 记住该节点所在的链表 */
	pxNewListItem->pvContainer = ( void * ) pxList;	/* 链表节点计数器++ */
	( pxList->uxNumberOfItems )++;
}

为了激活这一段代码运行,我把原来的测试代码中的一行代码做了修改:

如果分析没有错误,修改后的软件应该可以运行出来与之前一样的效果。运行效果如下:

从运行结果看,分析是准确的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2779687.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

第78讲 修改密码

系统管理实现 修改密码实现 前端 modifyPassword.vue&#xff1a; <template><el-card><el-formref"formRef":model"form":rules"rules"label-width"150px"><el-form-item label"用户名&#xff1a;&quo…

8种基本类型的包装类(与String的转换)

java针对8种基本数据类型&#xff0c;定义了相应的引用类型&#xff1a;包装类(封装类)&#xff0c;有了类的特点&#xff0c;就能调用类中的方法&#xff0c;java才是真正的面向对象。 基本数据类型 包装类byte Byteshort Shortint Integerlong Longfloat Floa…

HarmonyOS 状态管理装饰器 Observed与ObjectLink 处理嵌套对象/对象数组 结构双向绑定

本文 我们还是来说 两个 harmonyos 状态管理的装饰器 Observed与ObjectLink 他们是用于 嵌套对象 或者 以对象类型为数组元素 的数据结构 做双向同步的 之前 我们说过的 state和link 都无法捕捉到 这两种数据内部结构的变化 这里 我们模拟一个类数据结构 class Person{name:…

SpringCloud-微服务概述、SpringCloud入门概述、服务提供与消费

1.学习前言 1.1 学习前提 熟练使用SpringBoot 微服务快速开发框架了解过Dubbo Zookeeper 分布式基础电脑配置内存不低于8G 1.2 文章大纲 Spring Cloud 五大组件 服务注册与发现——Netflix Eureka负载均衡&#xff1a; ​ 客户端负载均衡——Netflix Ribbon ​ 服务端负载…

分享88个表单按钮JS特效,总有一款适合您

分享88个表单按钮JS特效&#xff0c;总有一款适合您 88个表单按钮JS特效下载链接&#xff1a;https://pan.baidu.com/s/1v-qcl8bv2kxZ8a98Xo9UAg?pwd8888 提取码&#xff1a;8888 Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 学习知识费力气&#xff0c;…

Java学习第十一节之命令行传参和断更原因

package method;public class Demo03 {public static void main(String[] args) {//args.length数组长度for (int i 0; i < args.length; i) {System.out.println("args[" i "]:"args[i]);}}}为什么没更新了&#xff1f; 家里有长辈生病了不好在医院照…

FreeRTOS.chg脚本出现意外状态

PE代码生成的时候遇到这么个问题 警报如下 Description Resource Path Location Type ERROR: Unexpected status of script: Beans\FreeRTOS\FreeRTOS.chg, please contact Freescale support. M18_BMCU FreeRTOS Processor Expert Problem 意思就是这个脚本文件有问题&…

【ES6】模块化

nodejs遵循了CommonJs的模块化规范 导入 require() 导出 module.exports 模块化的好处&#xff1a; 模块化可以避免命名冲突的问题大家都遵循同样的模块化写代码&#xff0c;降低了沟通的成本&#xff0c;极大方便了各个模块之间的相互调用需要啥模块&#xff0c;调用就行 …

【复现】大华 DSS SQL 注入漏洞_46

目录 一.概述 二 .漏洞影响 三.漏洞复现 1. 漏洞一&#xff1a; 四.修复建议&#xff1a; 五. 搜索语法&#xff1a; 六.免责声明 一.概述 大华DSS是大华的大型监控管理应用平台&#xff0c;支持几乎所有涉及监控等方面的操作&#xff0c;支持多级跨平台联网等操作。 可…

「数据结构」二叉搜索树1:实现BST

&#x1f387;个人主页&#xff1a;Ice_Sugar_7 &#x1f387;所属专栏&#xff1a;Java数据结构 &#x1f387;欢迎点赞收藏加关注哦&#xff01; 实现BST &#x1f349;二叉搜索树的性质&#x1f349;实现二叉搜索树&#x1f34c;插入&#x1f34c;查找&#x1f34c;删除 &am…

Linux防火墙开放

记录一次问题 写的网络服务无法通信 代码没问题&#xff0c;IP绑定、端口绑定没问题&#xff0c;就是无法进行通信&#xff0c;这里要分2步走。 服务器控制台开放 进入防火墙 添加规则&#xff0c;这里以开放udp的8899端口为例 这里在服务器后台就已经开放了&#xff0c;但此时…

问题:3【单选题】实现职业理想的一般步骤是()。 #媒体#媒体

问题&#xff1a;3【单选题】实现职业理想的一般步骤是()。 A、创业-立业-择业 B、择业-创业-立业 C、择业-立业-创业 D、立业-择业-创业 参考答案如图所示

LeetCode.144. 二叉树的前序遍历

题目 144. 二叉树的前序遍历 分析 这道题目是比较基础的题目&#xff0c;我们首先要知道二叉树的前序遍历是什么&#xff1f; 就是【根 左 右】 的顺序&#xff0c;然后利用递归的思想&#xff0c;就可以得到这道题的答案&#xff0c;任何的递归都可以采用 栈 的结构来实现…

小兔鲜项目网页版

头部模块 <!-- 头部模块 --><header><!-- 快捷菜单模块 --><div class"xtx-shortcut"><!-- 版心的盒子 --><nav class"container"><ul class"fr"><li><a href"#">请先登录<…

Linux——进程池(管道)

经过了管道的介绍之后&#xff0c;我们可以实现了进程间通信&#xff0c;现在我就来简单介 绍一下管道的应用场景——进程池。1. 引入 在我们的编码过程中&#xff0c;不乏会听到&#xff0c;内存池&#xff0c;进程池&#xff0c;空间配置器等等名词&#xff0c;这些是用来干…

专业140+总分410+华南理工大学811信号与系统考研经验华工电子信息与通信,真题,大纲,参考书。

23考研已经落幕&#xff0c;我也成功的上岸华工&#xff0c;回首这一年多的历程&#xff0c;也是有一些经验想和大家分享一下。 首先说一下个人情况&#xff0c;本科211&#xff0c;初试成绩400分。专业课140。 整体时间安排 对于考研&#xff0c;很重要的一环就是时间安排&…

AJAX——URL查询参数

1 URL查询参数 定义&#xff1a;浏览器提供给服务器的额外信息&#xff0c;让服务器返回浏览器想要的数据 语法&#xff1a;http://xxxx.com/xxx/xxx?参数名1值1 & 参数名2值2 2 axios-查询参数 语法&#xff1a;使用axios提供的 params 选项 注意&#xff1a;axios在…

微信小程序的了解和使用

微信小程序 微信小程序的项目组成 pages 文件夹 用于存放所有的小程序页面 logs 文件夹 用于存放所有的日志文件 utils 文件夹 用于存放工具性质的模块 js app.js 小程序的入口文件 app.json 小程序的全局配置文件 app.wxss 全局样式文件 project.config.json 项目配置文…

Docker 有哪些常用的命令和操作?

Docker是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的容器中&#xff0c;然后发布到任何流行的Linux机器或Windows机器上&#xff0c;也可以实现虚拟化。以下是Docker的一些常用命令和操作&#xff1a; 安装和启动Docker 要使用Do…

EMC学习笔记(二十四)降低EMI的PCB设计指南(四)

降低EMI的PCB设计指南&#xff08;四&#xff09; 1.电路板分区2.信号走线2.1 电容和电感串扰2.2 天线2.3 端接和传输线2.4输入端的阻抗匹配 tips&#xff1a;资料主要来自网络&#xff0c;仅供学习使用。 1.电路板分区 电路板分区与电路板平面规划具有相同的基本含义&#x…