链表中插入新的节点
/* 节点结构体定义 */
struct xLIST_ITEM
{TickType_t xItemValue; /* 辅助值,用于帮助节点做顺序排列 */ struct xLIST_ITEM * pxNext; /* 指向链表下一个节点 */ struct xLIST_ITEM * pxPrevious; /* 指向链表前一个节点 */ void * pvOwner; /* 指向拥有该节点的内核对象,通常是TCB */void * pvContainer; /* 指向该节点所在的链表 */
};
typedef struct xLIST_ITEM ListItem_t; /* 节点数据类型重定义 */
/* 链表结构体定义 */
typedef struct xLIST
{UBaseType_t uxNumberOfItems; /* 链表节点计数器 */ListItem_t * pxIndex; /* 链表节点索引指针 */MiniListItem_t xListEnd; /* 链表最后一个节点 */
} List_t;
/* 将节点插入到链表的尾部 */
void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
{ListItem_t * const pxIndex = pxList->pxIndex;pxNewListItem->pxNext = pxIndex; //1pxNewListItem->pxPrevious = pxIndex->pxPrevious; //2pxIndex->pxPrevious->pxNext = pxNewListItem; //3pxIndex->pxPrevious = pxNewListItem; //4/* 记住该节点所在的链表 */pxNewListItem->pvContainer = ( void * ) pxList; //5/* 链表节点计数器++ */( pxList->uxNumberOfItems )++; //6
}
学习过“野火”freeRTOS的童鞋们一定会对这些代码眼熟,我刚学,只是发表一下个人的观点和见解,大神可以直接跳过,写的理解可能只适用于刚接触链表有关知识点的小伙伴,不敢说通俗易懂,但是对于我本身这种菜鸟很容易接受和理解。重点讲解一下第三段代码的逻辑!!!!不喜欢可以敬请喷,我会及时当勉励的。
初始链表结构
假设链表当前结构如下(以便于理解,假设链表已有两个有效节点 A 和 B),并且 pxIndex 是伪头节点,它指向链表的末尾:
NULL <- A <-> B <-> pxIndex -> NULL
我们可以将链表节点看成是互相握手的小人,他们按照一定的顺序排列。我们一步步看看新小人 C 是如何加入到一群已经排好队的小人 A、B 和尾巴节点 pxIndex 中的。
初始状态
假设我们有一个双向链表队伍,它的排列如下:
A <-> B <-> pxIndex
这里的每个箭头 <-> 表示一个节点和它相邻节点之间的双向关系。pxIndex 可以看作是一个站在队伍最后的标记节点,不是真正的队伍成员。
- A:队伍中的第一个小人,没有人站在他前面。
- B:A 后面的一个小人。
- pxIndex:一个标记队伍末尾的小人,没有其他人站在他后面。
此时,队伍中的人数为 2(A 和 B),由变量 pxList->uxNumberOfItems 记录。
目标
现在,我们想让小人 C 加入队伍,站在 B 和 pxIndex 之间。最终目标是让队伍变成:
A <-> B <-> C <-> pxIndex
插入过程一步步拆解
1. 让 C 知道他后面是谁
代码的第一步是让 C 的 pxNext 指针指向 pxIndex,也就是告诉 C 站在他后面的是 pxIndex:
pxNewListItem->pxNext = pxIndex;
现在 C 的 pxNext 指针指向了 pxIndex,表示他知道了自己后面是队伍的尾巴节点。
A <-> B C -> pxIndex
2. 让 C 知道他前面是谁
接下来,我们需要告诉 C 站在他前面的是 B。我们通过 pxIndex->pxPrevious 找到 B,然后把 B 的地址赋给 C 的 pxPrevious 指针:
pxNewListItem->pxPrevious = pxIndex->pxPrevious;
现在 C 的 pxPrevious 指针指向了 B,表示 C 知道了他前面是谁。
A <-> B <-> C -> pxIndex
3. 让 B 知道他后面多了一个人
在队伍中,双向链表是彼此双向连接的。所以我们还需要告诉 B,让他知道后面站了一个新小人 C。我们通过 pxIndex->pxPrevious->pxNext 找到 B 的 pxNext,然后把 C 的地址赋给它:
pxIndex->pxPrevious->pxNext = pxNewListItem;
这样 B 的 pxNext 指针指向了 C,表示 B 知道他后面站了一个新成员 C。
A <-> B <-> C -> pxIndex
4. 更新队尾指针,表示 C 是最后一个人
队尾指针 pxIndex->pxPrevious 需要指向新插入的节点 C,表示队伍的最后一个有效成员是 C。因此,pxIndex->pxPrevious = pxNewListItem;:
pxIndex->pxPrevious = pxNewListItem;
现在 pxIndex->pxPrevious 指向了 C,表示 C 成为新的尾巴前的最后一个节点。
A <-> B <-> C <-> pxIndex
5. 记录新节点 C 的所属链表
我们把 pxList 的地址存储在 C->pvContainer 中,方便以后找到 C 属于哪个链表:
pxNewListItem->pvContainer = ( void * ) pxList;
6. 更新链表节点计数器
最后,链表节点数量增加了 1:
( pxList->uxNumberOfItems )++;
现在链表计数器变成了 3,准确记录了链表中有 A、B、C 三个节点。
总结
整个过程简单来说,就是让新节点 C 找到他前后的人,同时告诉他前后的人有了新成员 C,最后更新链表的头尾标记和计数器。
看到这里,你是不是已经完全理解和消化掉了如何插入新节点呢?会不会有一点点疑问,但是又不好意思问呢?我自我提问几个问题吧,因为太菜因为c学的不到位,所以就问题多多了一些。
Question One:
为什么pxIndex->pxPrevious代表的是B?????
答:
在这个链表结构中,pxIndex 是一个伪尾节点(或称标记节点),它作为链表的终点标记。因为 pxIndex 并不是真正的有效数据节点,它的作用只是指示链表的尾部。链表中的实际有效节点依次连接,而 pxIndex 在链表结构上只是方便操作的一个标记。
让我们来看看为什么 pxIndex->pxPrevious 会代表链表的最后一个有效节点 B。
伪尾节点(pxIndex)的工作方式
在这个双向链表中:
pxIndex总是指向链表的尾部,并且它的pxNext通常指向NULL或自己,以表示链表的终止。pxIndex->pxPrevious是链表最后一个有效节点。这是因为链表的逻辑结构让pxIndex成为尾节点标记,那么它的pxPrevious指针自然指向链表中真正的最后一个有效节点。
当前链表的结构示例
假设链表中有两个有效节点 A 和 B,链表的结构如下:
NULL <- A <-> B <-> pxIndex
在这个结构中:
A是链表的第一个有效节点,所以A->pxPrevious是NULL。B是链表中最后一个有效节点,所以B->pxNext指向pxIndex。pxIndex是链表的伪尾节点,所以pxIndex->pxPrevious指向B。
插入新节点的操作
当我们向链表末尾插入新节点 C 时,步骤如下:
- 新节点
C的pxPrevious被设置为当前最后一个有效节点,即pxIndex->pxPrevious,也就是B。 B->pxNext被更新为C,表示B的后面现在是C。pxIndex->pxPrevious更新为C,表示C成为链表中的新最后一个有效节点。
最终,链表结构变为:
NULL <- A <-> B <-> C <-> pxIndex
小结
因此,pxIndex->pxPrevious 总是指向链表的最后一个有效节点。这是因为 pxIndex 是一个伪尾节点,它自己没有有效数据,只是标记链表的终点。
Question Two:
pxIndex->pxPrevious->pxNext = pxNewListItem;为什么pxNewListItem是c的地址,pxNewListItem不是包括C的next和previous嘛?
答:
在这段代码中,pxNewListItem 是一个指向新节点的指针,表示整个新节点 C 的地址。这个指针本身包含了新节点的所有信息,包括它的 pxNext 和 pxPrevious 指针。
当我们写 pxIndex->pxPrevious->pxNext = pxNewListItem; 时,我们在做的实际上是更新链表中前一个节点的 pxNext 指针,使它指向新节点 C。这一步的关键在于理解双向链表节点之间的指针如何相互指向,并构成链表结构。
让我们深入理解一下这里的具体含义。
理解 pxNewListItem 的作用
-
pxNewListItem作为新节点的整体指针pxNewListItem指向整个节点C,即包含了C的pxNext和pxPrevious等信息。因此,pxNewListItem并不是指向C的某个特定成员,而是指向节点C本身的起始地址。 -
在链表中建立前后关系
在链表中,每个节点的pxNext和pxPrevious指针都用于指向相邻节点。这段代码:
pxIndex->pxPrevious->pxNext = pxNewListItem;
-
等效于
B->pxNext = pxNewListItem,其中B是pxIndex->pxPrevious指向的最后一个有效节点。这一步就是让B->pxNext指向新节点C的整体地址(即pxNewListItem),表示B的下一个节点现在是C。通过这一指向关系的设置,B和C之间的链接建立起来了。 -
为什么是整个
C的地址pxNewListItem的确包含C的pxNext和pxPrevious指针,但在这行代码中,我们只关心将pxNewListItem作为一个完整的节点地址赋给B->pxNext,表示B后面的节点是C。pxNext和pxPrevious的详细信息只有在访问节点C本身时才会被读取和使用。
代码工作流程小结
pxNewListItem->pxNext = pxIndex;:将C->pxNext指向pxIndex,表示C后面是伪尾节点pxIndex。pxNewListItem->pxPrevious = pxIndex->pxPrevious;:将C->pxPrevious指向B(原最后一个节点)。pxIndex->pxPrevious->pxNext = pxNewListItem;:将B->pxNext指向C,即B现在知道他后面是C。pxIndex->pxPrevious = pxNewListItem;:将pxIndex->pxPrevious更新为C,即C成为新的最后一个有效节点。
总结
在 pxIndex->pxPrevious->pxNext = pxNewListItem; 中,pxNewListItem 表示整个新节点 C 的地址,而不只是它的 pxNext 或 pxPrevious。我们通过这个赋值操作让 B->pxNext 指向 C 的地址,从而把 C 插入到 B 和 pxIndex 之间。
相关文章:
链表中插入新的节点
/* 节点结构体定义 */ struct xLIST_ITEM {TickType_t xItemValue; /* 辅助值,用于帮助节点做顺序排列 */ struct xLIST_ITEM * pxNext; /* 指向链表下一个节点 */ struct xLIST_ITEM * pxPrevious; /* 指向链表前一个节点 */ void * pvOw…...
AUTOSAR从入门到精通-BswM模块(二)
目录 前言 算法原理 BswM接口端口 BswM功能描述 模式仲裁 仲裁规则(Arbitration Rules) 模式仲裁来源 模式仲裁过程 模式条件(ModeCondition) 逻辑表达式(LogicExpressions) 模式控制 模式处理 操作执行 模式控制过程 模式控制基本流程 BswM Interfaces and …...
Spring DispatcherServlet详解
文章目录 Spring DispatcherServlet详解一、引言二、DispatcherServlet的初始化与工作流程1、DispatcherServlet的初始化1.1、加载配置和建立WebApplicationContext1.2、初始化策略 2、DispatcherServlet的工作流程2.1、请求分发2.2、代码示例 三、总结 Spring DispatcherServl…...
JS | 软件制作的流程是什么?
目录 一、 需求分析 二、 系统设计 三、 编码实现 四、 测试验证 五、 部署上线 六、 维护更新 软件制作的流程主要包含需求分析、系统设计、编码实现、测试验证、部署上线和维护更新。其中,需求分析是基础,它决定了软件的功能和性能;通…...
简单工厂模式
引言 简单工厂模式并不属于23种设计模式,它是工厂方法模式的“小弟”,由于日常编程中大家会经常用到,只不过没有察觉,因此下文将详解简单工厂模式。 1.概念 简单工厂模式(Simple Factory Pattern):又称为静态工厂方法(…...
【django】Django REST Framework 序列化与反序列化详解
目录 1、什么是序列化和反序列化? 2、Django REST Framework中的序列化和反序列化 3、安装与配置(第10章是从零开始) 3.1 安装 3.2 配置 4、基本使用 4.1 创建序列化器 4.2 使用序列化器(将数据序列化返回给前端ÿ…...
【Golang】Golang的Map的线程安全问题
文章目录 前言一、场景介绍二、线程安全的Map的使用四、总结 前言 在 Golang 编程中,map 是一种常用的数据结构,用于存储键值对。然而,Golang 的 map 在并发访问时是线程不安全的。如果多个 goroutine 同时读写同一个 map,可能会…...
指向指针的指针+ 值传递的理解
//17、下面的程序会出现什么结果 #include #include void getmemory(char *p) { p(char *) malloc(100); strcpy(p,”hello world”); } int main( ) { char *strNULL; getmemory(str); printf(“%s/n”,str); free(str); return 0; } // 程序崩溃,…...
CSS常用定位
一、relative 相对原先的位置进行定位 {position: relative;left: 50px; /* 相对原先位置左边的距离 */top: 100px; /* 相对原先位置上边的距离 */ } 二、absolute 绝对定位,是相对于最近有定位的父级元素进行定位 {position: absolute;righ…...
【Linux】从零开始使用多路转接IO --- select
碌碌无为,则余生太长; 欲有所为,则人生苦短。 --- 中岛敦 《山月记》--- 从零开始认识五种IO模型 1 前言2 认识多路转接select3 多路转接select等待连接4 完善代码5 总结 1 前言 上一篇文章我们讲解了五种IO模型的基本概念,并…...
ArcGIS Pro SDK (二十一)渲染
ArcGIS Pro SDK (二十一)渲染 文章目录 ArcGIS Pro SDK (二十一)渲染1 定义唯一值呈现器定义2 为最新观测值设置唯一值渲染器3 为先前的观测值设置唯一值渲染器4 设置简单的渲染器以绘制轨迹线5 检查先前的观测值和轨道线可见性6 使轨迹线和先前的观测点可见7 检索当前观测…...
FPGA在物联网边缘计算中的应用!!!
FPGA(现场可编程门阵列)在物联网边缘计算中的应用正变得越来越重要。边缘计算是一种分布式计算架构,它将数据的处理分散到网络的边缘,靠近数据源,而不是集中在数据中心处理。以下是FPGA在物联网边缘计算中的几个关键应…...
【解决】Linux环境中mysqlclient安装失败问题
问题描述 在Linux系统下安装myslclient报异常。系统为Centos 8 使用 pip install mysqlclient 报出下面的异常 error: subprocess-exited-with-error Getting requirements to build wheel did not run successfully.│ exit code: 1╰─> [30 lines of output]/bin/sh: pkg…...
✨ Midjourney中文版:创意启航,绘梦无界 ✨
Midjourney AI超强绘画 (原生态系统)用户端:Ai Loadinghttps://www.mjdiscord.com 项目详细介绍飞书文档:Docshttps://ivqklkndl4k.feishu.cn/docx/GRnMdCbcooWkwTx1RU4cZjGVnzb?fromfrom_copylk 🌐 无缝体验,中文定制…...
软件(1)
软件 常考软件 图像软件 Flash 一款二维动画处理软件 photoshop 图像处理界的“巨无霸” ACDSee ACDSee是常用的图片管理编辑软件,尽管也可以支持WAV格式的音频播放, 但目前主要是作为看图软件 音频软件 Winamp Winamp是数字媒体播放的先驱Audition Audi…...
linux perf 环境部署和基本测试(基于Ubuntu20.04)
1,linux 安装perf sudo apt-ge install linux-tools-common sudo apt-get install linux-tools-$(uname -r) linux-tools-generic -y 2 补充安装 sudo apt-get install python3-q-text-as-data 3,perf常用命令 larkubuntu:~$ perf usage: perf [--version] [--hel…...
【网络面试篇】HTTP(1)(笔记)——状态码、字段、GET、POST、缓存
目录 一、相关问题 1. HTTP请求常见的状态码和字段? (1)状态码 (2)字段 ① Host 字段 ② Content-length 字段 ③ Connection 字段 ④ Content-Type 字段 ⑤ Content-Encoding 字段 2. GET 和 POST 的区别&a…...
HTML 基础标签——分组标签 <div>、<span> 和基础语义容器
文章目录 1. `<div>` 标签特点用途示例2. `<span>` 标签特点用途示例3. `<fieldset>` 标签特点用途示例4. `<section>` 标签特点用途示例5. `<article>` 标签特点用途示例总结HTML中的分组(容器)标签用于结构化内容,将页面元素组织成逻辑区域…...
SS928V100 ISP常见问题列表
下载链接: https://download.csdn.net/download/quantum7/89948226 1 FAQ 1 1.1 ISP 1 1.1.1 如何解决整体锐度不足 1 1.1.2 如何解决图像发蒙问题,提高通透性 2 1.1.3 如何解决低照度清晰度差 2 1.1.4 如何解决图像清晰度与物体边缘白边和黑边问题…...
AI写诗:自动版大唐宫体诗
大唐学子,手拿一本小卷(类书),从中挑选过去他们(权威)认为好的词来拼接一首诗,此类诗词称作“宫体诗”,在初唐时期甚是流行。 写“宫体诗”的过程有木有那么一丝丝的熟悉?…...
Cesium1.95中高性能加载1500个点
一、基本方式: 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...
1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...
oracle与MySQL数据库之间数据同步的技术要点
Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异,它们的数据同步要求既要保持数据的准确性和一致性,又要处理好性能问题。以下是一些主要的技术要点: 数据结构差异 数据类型差异ÿ…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...
CocosCreator 之 JavaScript/TypeScript和Java的相互交互
引擎版本: 3.8.1 语言: JavaScript/TypeScript、C、Java 环境:Window 参考:Java原生反射机制 您好,我是鹤九日! 回顾 在上篇文章中:CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...
C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
Spring数据访问模块设计
前面我们已经完成了IoC和web模块的设计,聪明的码友立马就知道了,该到数据访问模块了,要不就这俩玩个6啊,查库势在必行,至此,它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据(数据库、No…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...
