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

news2025/1/17 21:46:39

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://www.coloradmin.cn/o/1442132.html

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

相关文章

101. 对称二叉树 - 力扣(LeetCode)

题目描述 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 题目示例 输入&#xff1a;root [1,2,2,3,4,4,3] 输出&#xff1a;true 解题思路 首先想清楚&#xff0c;判断对称二叉树要比较的是哪两个节点&#xff0c;要比较的可不是左右节点&#xff01; 对于…

SAP-PS-001-006问题预算占用与订单实际金额不一致

前言 PS模块最复杂的业务场景主要就是ETO&#xff08;Engineering-To-Order&#xff09;&#xff0c;也就是边设计边生产边采购的三边业务。 意味着从前端设计开始的成本就已经要进行收集&#xff0c;其次对于大型非标设备的生产发货只是一个环节&#xff0c;发货后还会涉及到现…

Java项目maven打包的包名设置(finalname标签的使用)

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

年-月-日的输入方法

大家对于输入的函数一定有所认识&#xff0c;比如c中位于 #include <iostream> 中的 cin 函数&#xff0c;这个函数输入单个十分好用&#xff0c;但是对于年月日这种较为复杂的就行不通了&#xff0c;就只能输入最前面的一个 那怎么输入像这样的年月日呢 答案就是用 scan…

JVM-双亲委派机制

双亲委派机制定义 双亲委派机制指的是&#xff1a;当一个类加载器接收到加载类的任务时&#xff0c;会自底向上查找是否加载过&#xff0c; 再由顶向下进行加载。 详细流程 每个类加载器都有一个父类加载器。父类加载器的关系如下&#xff0c;启动类加载器没有父类加载器&am…

C++2024寒假J312实战班2.6

错题列表&#xff1a; #1Playlist #2分数线划定 #3Made Up #4图书管理员 #1Playlist 我们来介绍滑动窗口的写法&#xff1a; 1、使用一个滑动窗口k[l,r)在歌曲列表中移动。 2、同时利用一个unordered_set S来检测窗口中的歌曲是否有重复。如果窗口右端的歌曲在窗口内没有重复…

LLM应用开发与落地:流式响应

一、背景 最近智能客服产品给到一个游戏客户那边&#xff0c;客户那边的客服负责人体验后认为我们产品回答的准确率是还是比较高的。同时&#xff0c;他反馈了几个需要改进的地方&#xff0c;其中一个就是机器人回复慢。机器人回复慢有很多原因&#xff0c;也有优化方式&#…

2019 年全国职业院校技能大赛高职组 “信息安全管理与评估”赛项任务书(笔记详解)

1. 网络拓扑图 2. IP 地址规划表 3. 设备初始化信息 阶段一 任务 1:网络平台搭建 1、根据网络拓扑图所示,按照 IP 地址参数表,对 DCFW 的名称、各接口IP 地址进行配置。 2、根据网络拓扑图所示,按照 IP 地址参数表,对 DCRS 的名称进行配置,创建 VLAN 并将相应接口划入 …

C#,雷卡曼数(Recamán Number)的算法与源代码

1 雷卡曼数&#xff08;Recamn Number&#xff09; 雷卡曼数&#xff08;Recamn Number&#xff09;&#xff0c;即Recaman序列被定义如下&#xff1a; (1) a[0]0; (2) 如果a[m-1]-m>0并且这个值在序列中不存在&#xff0c;则a[m]a[m-1]-m; (3) 否则a[m]a[m-1]m; 雷卡曼序…

[UI5 常用控件] 08.Wizard,NavContainer

文章目录 前言1. Wizard1.1 基本结构1.2 属性1.2.1 Wizard&#xff1a;complete1.2.2 Wizard&#xff1a;finishButtonText1.2.3 Wizard&#xff1a;currentStep1.2.4 Wizard&#xff1a;backgroundDesign1.2.5 Wizard&#xff1a;enableBranching1.2.6 WizardStep&#xff1a;…

【Linux】vim的基本操作与配置(下)

Hello everybody!今天我们继续讲解vim的操作与配置&#xff0c;希望大家在看过这篇文章与上篇文章后都能够轻松上手vim! 1.补充 在上一篇文章中我们说过了&#xff0c;在底行模式下set nu可以显示行号。今天补充一条&#xff1a;set nonu可以取消行号。这两条命令大家看看就可…

LeetCode-第28题-找出字符串中第一个匹配项的下标

1.题目描述 给你两个字符串 haystack 和 needle &#xff0c;请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标&#xff08;下标从 0 开始&#xff09;。如果 needle 不是 haystack 的一部分&#xff0c;则返回 -1 。 2.样例描述 3.思路描述 可以让字符串 …

SpringBoot源码解读与原理分析(二十)IOC容器的刷新(一)

文章目录 7 IOC容器的刷新7.1 初始化前的预处理7.1.1 初始化属性配置7.1.2 初始化早期事件的集合 7.2 初始化BeanFactory7.2.1 注解驱动的refreshBeanFactory7.2.2 XML驱动的refreshBeanFactory7.2.3 获取BeanFactory 7.3 BeanFactory的预处理配置7.3.1 ApplicationContextAwar…

自然语言学习nlp 六

https://www.bilibili.com/video/BV1UG411p7zv?p118 Delta Tuning&#xff0c;尤其是在自然语言处理&#xff08;NLP&#xff09;和机器学习领域中&#xff0c;通常指的是对预训练模型进行微调的一种策略。这种策略不是直接更新整个预训练模型的权重&#xff0c;而是仅针对模型…

vue3+vite+ts 配置commit强制码提交规范配置 commitlint

配置 git 提交时的 commit 信息&#xff0c;统一提交 git 提交规范 安装命令: npm install -g commitizen npm i cz-customizable npm i commitlint/config-conventional commitlint/cli -D 文件配置 根路径创建文件 commitlint.config.js module.exports {// 继承的规…

【开源】基于JAVA+Vue+SpringBoot的假日旅社管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统介绍2.2 QA 问答 三、系统展示四、核心代码4.1 查询民宿4.2 新增民宿评论4.3 查询民宿新闻4.4 新建民宿预订单4.5 查询我的民宿预订单 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的假日旅社…

STL之list容器的介绍与模拟实现+适配器

STL之list容器的介绍与模拟实现适配器 1. list的介绍2. list容器的使用2.1 list的定义2.2 list iterator的使用2.3 list capacity2.4 list element access2.5 list modifiers2.6 list的迭代器失效 3. list的模拟实现3.1 架构搭建3.2 迭代器3.2.1 正向迭代器3.2.2反向迭代器适配…

鸿蒙开发(六)布局概述

迄今为止&#xff0c;我还没有正式提到布局的概念。但其实我之前的demo里面&#xff0c;已经默认使用到了一种布局&#xff0c;那就是线性布局&#xff08;Row、Column&#xff09;&#xff0c;这也是DevEco创建项目默认页面里面默认采用的布局。那么本篇&#xff0c;带着大家一…

PyTorch 2.2 中文官方教程(十)

使用整体追踪分析的追踪差异 原文&#xff1a;pytorch.org/tutorials/beginner/hta_trace_diff_tutorial.html 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 作者: Anupam Bhatnagar 有时&#xff0c;用户需要识别由代码更改导致的 PyTorch 操作符和 CUDA 内核的变化…

python 动态数据 展示 ,数据是由51单片机发送过来的,温度传感器。

import tkinter as tk import randomimport seriallis[] for i in range(50):lis.append(i1) # 打开串行端口 ser serial.Serial(COM3, 9600) # 9600为波特率&#xff0c;根据实际情况进行调整# 初始化数据 lis [random.randint(15, 35) for _ in range(50)]def update_data…