FreeRTOS实战指南 — 3.1 C语言链表

目录

1 单向链表

1.1 单链表的概念

1.2 链表增加头结点的作用

1.3 单链表的实现

2 循环链表

3 双向链表


为什么学习链表?FreeRTOS使用链表来管理任务调度,来维护不同优先级的就绪任务;许多内部数据结构,如任务控制块(TCB)也使用了链表来组织和管理;链表也被用于管理FreeRTOS中的资源,如信号量、消息队列等。使用链表能够灵活地添加、删除和遍历这些数据,从而实现高效的任务管理和调度。

1 单向链表

1.1 单链表的概念

单链表是一种基本的线性数据结构,它由一系列节点组成,每个节点包含两部分数据和指针两部分。数据部分存储节点的值,而指针部分存储指向列表中下一个节点的引用。单链表的特点是只能从首节点开始,通过逐个访问每个节点的指针来遍历整个列表。

图 单链表示意图

节点(Node):是链表的基本单元,每个节点包含数据域和指针域。数据域存储数据,指针域存储指向下一个节点的指针。节点中有几个容易混淆的概念,下面的辨析可以帮助透彻理解链表。

头节点(Head):是链表的第一个节点,头节点的指针域指向第一个实际的数据节点,或者在空链表中为NULL。尾节点(Tail)是链表的最后一个节点,其指针域通常指向NULL,表示链表的结束。

首元节点:链表中存储第一个数据元素的节点被称为首元节点。

头指针:头指针是一个特殊的指针,它指向链表的第一个结点的指针,若链表没有投结点,则头指针所指结点为链表的首元结点,如果链表为空,头指针通常被设置为null。

1.2 链表增加头结点的作用

便于首元结点的处理:在没有头结点的链表中,如果要删除首元结点,需要更新头指针指向下一个节点,增加头结点后,对首元结点的插入和删除操作可以和其他节点的操作一样处理,无需特殊逻辑。

(a)没有头结点

(b)有头结点

便于空表和非空表的统一处理:在没有头结点的链表中,需要区分链表是否为空。插入新节点时,如果链表为空(头指针为null),则新节点成为首元结点;如果链表非空,则新节点插入到首元结点之后。增加头结点后,链表的插入和删除操作可以统一处理,无需区分链表是否为空。头结点始终存在,头指针始终指向头结点,这样就避免了对空链表的额外检查。

1.3 单链表的实现

定义链表结构体:单链表通过一系列节点(Node)来存储数据,每个节点不仅包含数据部分,还包含一个指向下一个节点的指针。

// 定义链表节点的结构体
typedef struct ListNode {int value;             // 存储数据struct ListNode *next; // 指向下一个节点的指针
} ListNode;

实现链表的插入操作:如果链表为空(即头指针 *head 为 NULL),新节点直接成为链表的头节点。如果链表不为空,函数会遍历链表直到找到最后一个节点,然后将新节点链接到链表的末尾。

// 在链表末尾插入一个新的节点
void insertListNode(ListNode **head, int value) 
{// 动态分配内存空间给新节点ListNode *newNode = (ListNode *)malloc(sizeof(ListNode));// 如果内存分配失败,打印错误信息并返回if (newNode == NULL) {fprintf(stderr, "Memory allocation failed\n");return;}// 将传入的值赋给新节点赋值newNode->value = value;newNode->next = NULL;// 如果链表的头指针是NULL,说明链表为空,直接将头指针指向新节点if (*head == NULL) {*head = newNode;} else {// 如果链表不为空,遍历链表找到最后一个节点ListNode *current = *head;while (current->next != NULL) {current = current->next;}// 将最后一个节点的next指针指向新节点,完成插入current->next = newNode;}
}

实现链表的删除操作:首先遍历链表,寻找值匹配的节点。如果找到,它会根据该节点是否是头节点来更新头指针或前一个节点的 next 指针,从而从链表中移除该节点。如果链表中没有找到匹配值的节点,则打印一条消息表示未找到。最后,释放被删除节点的内存。

// 用于删除链表中值为指定值的节点
void deleteListNode(ListNode **head, int value) {// current用于遍历链表,初始指向头节点ListNode *current = *head;// prev用于指向当前节点的前一个节点,初始为NULLListNode *prev = NULL;// 遍历链表,寻找值为指定值的节点while (current != NULL && current->value != value) {prev = current;            // 更新prev为当前节点current = current->next;  // 移动current到下一个节点}// 如果current为NULL,说明没有找到值为指定值的节点if (current == NULL) {printf("Value %d not found in the list\n", value);return;}// 如果prev为NULL,说明要删除的是头节点if (prev == NULL) {*head = current->next;  // 将头指针指向头节点的下一个节点} else {// 如果prev不为NULL,说明要删除的不是头节点prev->next = current->next;  // 将前一个节点的next指针指向要删除节点的下一个节点}// 释放被删除节点的内存free(current);
}

2 循环链表

循环链表的最后一个节点的指针不是指向 NULL,而是指向链表的第一个节点,形成一个环。这种结构使得链表的遍历可以连续进行,直到再次回到起点。

循环单链表的操作和单链表基本一致,差别仅在于:当链表遍历时,判别当前指针p是否指向表尾结点的终止条件不同。在单链表中,判别条件为p!=NULL或p->next!=NULL,而循环单链表的判别条件为p != 头指针或p->next != 头指针。

3 双向链表

双向链表每个节点包含数据部分和两个指针,分别指向前一个节点和后一个节点,这种结构可以在链表中向前或向后遍历。

定义双向链表:双向链表每个节点包含一个整数数据和两个指针,分别指向链表中的前一个节点和后一个节点。这种结构使得在链表中向前或向后遍历以及插入和删除节点变得更加高效。

/* 定义双向链表 */
typedef struct DouLinkNode {int data;                 // 存储节点数据struct DouLinkNode *prev; // 指向前一个节点的指针struct DouLinkNode *next; // 指向下一个节点的指针
} DouLinkNode;

插入结点:在双向链表的末尾插入一个新的节点。如果链表为空,新节点成为头节点。如果链表不为空,遍历链表找到最后一个节点,并将新节点链接到末尾,同时更新最后一个节点的 next 指针和新节点的 prev 指针。

// 定义一个函数,用于在双向链表末尾插入一个新的节点
void insertDouLinkNode(DouLinkNode **head, int data)
{// 创建一个新的节点,存储给定的数据DouLinkNode* newNode = createDouLinkNode(data);// 如果内存分配失败,则返回if (newNode == NULL) return;// 如果链表为空,新节点成为头节点if (*head == NULL) {*head = newNode;} else {// 如果链表不为空,找到链表的最后一个节点DouLinkNode* current = *head;// 遍历链表直到最后一个节点while (current->next != NULL) {current = current->next;}// 将新节点链接到链表的末尾current->next = newNode;// 设置新节点的前驱指针newNode->prev = current;}
}

删除结点:从双向链表中删除一个具有特定数据值的节点。首先检查头节点是否是要删除的节点,如果是,更新头节点并释放原头节点的内存。如果不是,遍历链表找到要删除的节点,然后根据节点的位置更新前驱和后继节点的指针,并释放要删除节点的内存。

// 定义一个函数,用于从双向链表中删除一个具有特定数据值的节点
void deleteDouLinkNode(DouLinkNode **head, int data) {// 初始化当前节点指针为头节点DouLinkNode *current = *head, *temp = NULL;// 如果链表为空,则不执行任何操作if (*head == NULL) return;// 如果头节点就是要删除的节点if (current->data == data) {// 更新头节点为下一个节点*head = current->next;// 如果新的头节点不为空,更新其前驱指针为NULLif (*head != NULL) {(*head)->prev = NULL;}// 释放原头节点的内存free(current);return;}// 遍历链表寻找要删除的节点while (current != NULL && current->data != data) {current = current->next;}// 如果没有找到要删除的节点,返回if (current == NULL) return;// 保存要删除节点的前一个节点temp = current->prev;// 如果要删除的节点后面还有节点,更新后一个节点的前驱指针if (current->next != NULL) {current->next->prev = temp;} else {// 如果没有后继节点,说明当前节点是最后一个节点,更新前一个节点的后继指针为NULLtemp->next = NULL;}// 释放要删除节点的内存free(current);
}

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

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

相关文章

机器学习 vs 深度学习:深入浅出解析两者的区别

在当今科技飞速发展的时代,**机器学习(Machine Learning)和深度学习(Deep Learning)**成为了人工智能(AI)领域的热门话题。无论你是技术专家、学生,还是对AI感兴趣的普通读者&#x…

Pointnet++改进57:全网首发SCSA(2024最新注意力机制)|即插即用,提升特征提取模块性能

简介:1.该教程提供大量的首发改进的方式,降低上手难度,多种结构改进,助力寻找创新点!2.本篇文章对Pointnet++特征提取模块进行改进,加入SCSA注意力机制,提升性能。3.专栏持续更新,紧随最新的研究内容。 目录 1.理论介绍 2.修改步骤 2.1 步骤一 2.2 步骤二 2.3 步骤…

搭子app有哪些?找搭子用什么软件?8款找搭子平台清单分享

在这个快节奏的现代社会,人们常常渴望找到志同道合的伙伴,一同分享生活中的喜怒哀乐、探索未知的领域。而找搭子软件的出现,如同璀璨星辰照亮了我们的社交天空。下面就为你带来一份精心整理的找搭子软件清单。 1. 咕哇找搭子小程序&#xff1…

2----手机维修工具 集合解锁 修复参数 刷机支持高通 MTK 展讯等芯片 支持一些PDA设备

这款工具在早些年使用较普遍. 。支持的机型非常多。不但支持国内品牌机型还支持很多国外机。总计多达几百种型号。功能选项较多。唯一的缺点是英文版。需要一定的英文基础的友友使用。支持各类机型修复系统 修复参数 读取信息 备份分区等等。以及一些小品牌机型的root 备份基带…

RT-DETR改进策略:BackBone改进|Swin Transformer,最强主干改进RT-DETR

摘要 在深度学习与计算机视觉领域,Swin Transformer作为一种强大的视觉Transformer架构,以其卓越的特征提取能力和自注意力机制,正逐步引领着图像识别与检测技术的革新。近期,我们成功地将Swin Transformer引入并深度整合至RT-DERT(一种高效的实时目标检测与识别框架)中…

解决iframe嵌套第三方页面被拒绝

背景 很多时候,出于安全考虑,没有第三方页面的允许,我们是无法直接通过iframe去打开别人的第三方页面的,通常他们会通过在页面请求的响应头增加X-Frame-Options (去了解)和Content-Security-Policy (去了解)。 目的 可是有些时…

尚品汇-秒杀商品存入缓存、Redis发布订阅实现状态位(五十一)

目录: (1)秒杀业务分析 (2)搭建秒杀模块 (3)秒杀商品导入缓存 (4)redis发布与订阅实现 (1)秒杀业务分析 需求分析 所谓“秒杀”&#xff0…

I2C/IIC学习笔记

I2C/IIC 有些同学I2C和IIC分不清,I2C和IIC实际上是指同一种通信协议。I2C是Inter-Integrated Circuit的缩写,而IIC是它的另一种表述方式,代表的是同一个意思,即“集成电路间总线”。I2C是一种由飞利浦公司(现恩智浦半…

【题解】【枚举】—— [USACO1.5] 回文质数 Prime Palindromes

【题解】【枚举】—— [USACO1.5] 回文质数 Prime Palindromes [USACO1.5] 回文质数 Prime Palindromes题目描述输入格式输出格式输入输出样例输入 #1输出 #1 提示 思路1.素数筛法1.1.思路解析1.2.参考代码 解法1.打表1.1.思路解析1.2.AC代码 解法2.构造回文数2.1.思路解析2.2.…

Matlab Simulink 主时间步(major time step)、子时间步(minor time step)

高亮颜色说明:突出重点 个人觉得,:待核准个人观点是否有误 高亮颜色超链接 文章目录 对Simulink 时间步的理解Simulink 采样时间的类型Discrete Sample Times(离散采样时间)Controllable Sample Time(可控采样时间) Continuous Sample Times(…

基于springboot大学生就业招聘系统的设计与实现

大学生就业招聘系统的设计与实现 摘要 随着信息互联网信息的飞速发展,大学生就业成为一个难题,好多公司都舍不得培养人才,只想要一专多能之人才,不愿是承担社会的责任,针对这个问题开发一个专门适应大学生就业招聘的…

HTML+CSS - 网页布局之多列布局定位

1. 多列布局 CSS中多列布局处理文本内容&#xff0c;特别适合对于长段落或者大量文本进行自动分栏显示 类似于grid分布&#xff0c;但相较之下更加简洁明了 基本语法 <div class"container"><p>这是一些示例文本&#xff0c;当我们使用 column-count…

CGAL GIS 应用 - 从点云到DTM

CGAL GIS 应用 - 从点云到DTM GIS应用中使用的许多传感器(例如激光雷达)都会生成密集的点云。此类应用通常利用更高级的数据结构:例如&#xff0c;不规则三角网(TIN)&#xff0c;它可以作为数字高程模型(DEM)的基础&#xff0c;特别是用于生成数字地形模型(DTM)。 点云也可以通…

SOMEIP_ETS_111: SD_Empty_Entries_Array

测试目的&#xff1a; 验证DUT能够忽略声明了条目数组长度为零的SubscribeEventgroup消息。 描述 本测试用例旨在确保DUT在接收到一个Entries数组长度为零的SubscribeEventgroup消息时&#xff0c;能够正确地忽略该消息&#xff0c;不对其进行解释或响应。 测试拓扑&#x…

移动UI案例:工具类app整套案例

工具类App是指提供各种实用工具和功能的手机应用程序。这些工具可以包括但不限于日历、闹钟、备忘录、翻译、计算器、单位转换、天气预报、地图导航、音乐播放器、相机、视频编辑等。这些工具类App能够帮助用户解决日常生活和工作中的各种问题&#xff0c;提高效率和便利性。 …

基于是springboot小区物业管理系统

小区物业管理系统 摘 要 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff1b;对于小区物业管理系统当然也不能排除在外&#xff0c;随着网络技术的不断成熟&#xff0c;带动了小区物业管理系统&#x…

Redis的存储原理和数据模型

一、Redis是单线程还是多线程呢&#xff1f; 我们通过跑redis的代码&#xff0c;查看运行的程序可以得知&#xff0c;Redis本身其实是个多线程&#xff0c;其中包括redis-server&#xff0c;bio_close_file&#xff0c;bio_aof_fsync&#xff0c;bio_lazy_free&#xff0c;io_t…

猫头虎分享:Python库 SQLAlchemy 的简介、安装、用法详解入门教程

&#x1f42f; 猫头虎分享&#xff1a;Python库 SQLAlchemy 的简介、安装、用法详解入门教程 大家好&#xff0c;我是猫头虎&#xff01;今天有粉丝问猫哥&#xff1a;“在项目开发中如何高效地进行数据库操作&#xff1f;是否有一个灵活又强大的ORM库推荐&#xff1f;”正好&…

[Linux] 进程优先级 进程的调度与切换 环境变量详解

进程优先级 && 进程的调度与切换 && 环境变量 1.进程优先级1.1查看进程1.2 PRI VS NI1.3用指令调整优先级 2.进程的调度与切换2.1 进程切换2.2 linux实现进程调度的算法 3.环境变量前言引入&#xff08;main参数--命令行参数&#xff09;3.1 环境变量3.2 PATH环…

题目:单调栈

1、关于栈的概述 栈是一种数据结构&#xff0c;遵循“后进先出”&#xff08;LIFO, Last In, First Out&#xff09;的原则。这意味着最后被插入栈中的元素会最先被移除。可以把它想象成一个垒盘子的情况&#xff0c;新的盘子总是放在最上面&#xff0c;而最上面的盘子会最先被…