链表中插入新的节点
/* 节点结构体定义 */
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写诗:自动版大唐宫体诗
大唐学子,手拿一本小卷(类书),从中挑选过去他们(权威)认为好的词来拼接一首诗,此类诗词称作“宫体诗”,在初唐时期甚是流行。 写“宫体诗”的过程有木有那么一丝丝的熟悉?…...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...

前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)
骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术,它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton):由层级结构的骨头组成,类似于人体骨骼蒙皮 (Mesh Skinning):将模型网格顶点绑定到骨骼上,使骨骼移动…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台
🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

短视频矩阵系统文案创作功能开发实践,定制化开发
在短视频行业迅猛发展的当下,企业和个人创作者为了扩大影响力、提升传播效果,纷纷采用短视频矩阵运营策略,同时管理多个平台、多个账号的内容发布。然而,频繁的文案创作需求让运营者疲于应对,如何高效产出高质量文案成…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...
GitHub 趋势日报 (2025年06月06日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 590 cognee 551 onlook 399 project-based-learning 348 build-your-own-x 320 ne…...

Golang——9、反射和文件操作
反射和文件操作 1、反射1.1、reflect.TypeOf()获取任意值的类型对象1.2、reflect.ValueOf()1.3、结构体反射 2、文件操作2.1、os.Open()打开文件2.2、方式一:使用Read()读取文件2.3、方式二:bufio读取文件2.4、方式三:os.ReadFile读取2.5、写…...