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 )++;
}

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

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深度学习中常用激活函数介绍

深度学习中常用激活函数介绍 在深度学习中&#xff0c;激活函数的作用主要是引入非线性特性&#xff0c;提高模型的表达能力。具体如下&#xff1a; 解决线性不可分问题&#xff1a;激活函数可以将输入特征的复杂度提升&#xff0c;使得神经网络能够处理非线性问题&#xff0c…

分布式系统架构介绍

1、为什么需要分布式架构&#xff1f; 增大系统容量&#xff1a;单台系统的性能瓶颈&#xff0c;多台机器才能应对大规模的应用场景&#xff0c;所以就需要我们的应用支撑平台具备分布式架构。 加强系统的可用&#xff1a;为了满足业务的SLA要求&#xff0c;需要通过分布式架构…

第62讲商品搜索动态实现以及性能优化

商品搜索后端动态获取数据 后端动态获取数据&#xff1a; /*** 商品搜索* param q* return*/GetMapping("/search")public R search(String q){List<Product> productList productService.list(new QueryWrapper<Product>().like("name", q)…

Java学习笔记2024/2/8

面向对象 //面向对象介绍 //面向: 拿、找 //对象: 能干活的东西 //面向对象编程: 拿东西过来做对应的事情 //01-如何设计对象并使用 //1.类和对象 //2.类的几个不错注意事项 1. 类和对象 1.1 类和对象的理解 客观存在的事物皆为对象 &#xff0c;所以我们也常常说万物皆对…

机器学习 | 深入集成学习的精髓及实战技巧挑战

目录 xgboost算法简介 泰坦尼克号乘客生存预测(实操) lightGBM算法简介 《绝地求生》玩家排名预测(实操) xgboost算法简介 XGBoost全名叫极端梯度提升树&#xff0c;XGBoost是集成学习方法的王牌&#xff0c;在Kaggle数据挖掘比赛中&#xff0c;大部分获胜者用了XGBoost。…

Java后端技术助力,党员学习平台更稳定

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

大模型2024规模化场景涌现,加速云计算走出第二增长曲线

导读&#xff1a;2024&#xff0c;大模型第一批规模化应用场景已出现。 如果说“百模大战”是2023年国内AI产业的关键词&#xff0c;那么2024年我们将正式迈进“应用为王”的新阶段。 不少业内观点认为&#xff0c;2024年“百模大战”将逐渐收敛甚至洗牌&#xff0c;而大模型在…

video / image上传操作-校验、截取首帧和正方形预览图等

常见video / image上传操作-校验、截取首帧和正方形预览图等。 上回搞了一个视频和图片上传和校验的需求&#xff0c;感觉学到很多&#xff0c;一些常见的函数记录如下&#xff1a; 1. 图片校验尺寸 const { maxCount 30, maxWidth, maxHeight, minHeight 200, minWidth …

Java基础知识练习题

1.对Java源文件进行编译操作的命令是&#xff08;B&#xff09; A.Java B.javac C.where is java D.javaw 2.下列命令中&#xff0c;用来运行Java程序的是&#xff08;A&#xff09;A.java B. javadoc C. jar D. javac 分析&#xff1a; 对Java源程序进行编译的命令是J…

力扣102. 二叉树的层序遍历 (复习vector和queue的常见用法

目录 题目描述 题目解析 题目答案 题目所用知识点 最后 题目描述 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术…

使用pygame生成红包封面

import pygame import sys# 初始化pygame pygame.init()# 设置红包封面尺寸 size width, height 640, 960 screen_color (255, 0, 0) # 红色背景# 创建窗口 screen pygame.display.set_mode(size) pygame.display.set_caption(红包封面)# 加载龙形图片 dragon_image pygam…

一些参数(仅供个人理解)

1.mAP&#xff1a; 数据集的平均准确率 mAP50-95&#xff1a;mAP阈值为50到mAP阈值为95&#xff0c;间隔5%,取得10个mAP值&#xff0c;然后对这十个值取平均。 目标检测评估指标mAP&#xff1a;从Precision,Recall,到AP50-95【未完待续】_map50和map50-95-CSDN博客 2.IoU&a…

JVM调优(Window下)

1、编写代码&#xff0c;像下面代码这样&#xff0c;产生OOM&#xff0c; private static final Integer K 1024;/*** 死循环&#xff0c;验证JVM调优* return*/GetMapping(value "/deadLoop")public void deadLoop(){int size K * K * 8;List<byte[]> lis…

C语言第二十一弹---指针(五)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 转移表 1、转移表 总结 1、转移表 函数指针数组的用途&#xff1a;转移表 举例&#xff1a;计算器的⼀般实现&#xff1a; 假设我们需要做一个能够进行加减…

Flume拦截器使用-实现分表、解决零点漂移等

1.场景分析 使用flume做数据传输时&#xff0c;可能遇到将一个数据流中的多张表分别保存到各自位置的问题&#xff0c;同时由于采集时间和数据实际发生时间存在差异&#xff0c;因此需要根据数据实际发生时间进行分区保存。 鉴于此&#xff0c;需要设计flume拦截器配置conf文件…

C#,佩尔数(Pell Number)的算法与源代码

1 佩尔数&#xff08;Pell Number&#xff09; 佩尔数&#xff08;Pell Number&#xff09;是一个自古以来就知道的整数数列&#xff0c;由递推关系定义&#xff0c;与斐波那契数类似。佩尔数呈指数增长&#xff0c;增长速率与白银比的幂成正比。它出现在2的算术平方根的近似值…

一图窥探RAG技术发展现状

2023年除了大语言模型&#xff0c;听到最多的当属RAG&#xff08;检索增强生成技术了&#xff09;&#xff0c;在实际业务场景落地过程中&#xff0c;由于大模型目前的一定局限和能力现状以及Token限制、训练成本等多种因素的影响下&#xff0c;RAG不得不成为大家选择快速试错、…

WebSocket+Http实现功能加成

WebSocketHttp实现功能加成 前言 首先&#xff0c;WebSocket和HTTP是两种不同的协议&#xff0c;它们在设计和用途上有一些显著的区别。以下是它们的主要特点和区别&#xff1a; HTTP (HyperText Transfer Protocol): 请求-响应模型&#xff1a; HTTP 是基于请求-响应模型的协…

Three.js学习8:基础贴图

一、贴图 贴图&#xff08;Texture Mapping&#xff09;&#xff0c;也翻译为纹理映射&#xff0c;“贴图”这个翻译更直观。 贴图&#xff0c;就是把图片贴在 3D 物体材质的表面&#xff0c;让它具有一定的纹理&#xff0c;来为 3D 物体添加细节的一种方法。这使我们能够添加…

TCP和UDP相关问题(重点)——7.TCP的流量控制怎么实现的?

流量控制就是在双方通信时&#xff0c;发送方的速率和接收方的速率不一定是相等的&#xff0c;如果发送方发送的太快&#xff0c;接收方就只能把数据先放到接收缓冲区中&#xff0c;如果缓冲区都满了&#xff0c;那么处理不过来就只能丢弃&#xff0c;所以需要控制发送方的速率…