1-1 C语言链表
目录
目录
1.0 定义
2.0 为什么使用链表
3.0 链表原理
4.0 创建链表节点
5.0 链表原理续
6.0 链表实现
6.0.1 创建节点
6.0.2 初始化链表
6.0.3 添加链表节点
6.0.4 循环遍历
6.0.5 插入节点
6.0.6 插入头结点main函数
7.0 完整代码
8.0 节点添加方案二
8.0.1 循环遍历关键点
8.0.2 添加节点的关键
9.0 遍历链表节点
10.0 程序实现
10.0.1 创建链表节点
10.0.2 初始化链表
10.0.3 模拟节点数据
10.0.4 添加节点
10.0.5 打印节点
10.0.6 main函数部分
1.0 定义
链表是由一系列节点组成,每个节点包含数据域和指针域,线性表的顺序存储用一块连续的内存空间,线性表的链式存储有一块不连续的存储空间。
2.0 为什么使用链表
智能网关需要具备动态管理子设备的功能,注册、添加、删除、获取数据等等,如何设计网关软件的数据结构,管理所有的子设备,假如使用TempHumisensor结构体表示单点子设备的数据:
如果使用数组进行管理存在一个问题,元素数量如何定义,不同环境部署的子设备数量是有差异的,太大会浪费内存太小会导致子设备数量受限,需要找到一种按需分配的方案,我们首先会想到指针和动态内存:
各子设备对应的动态内存不像数组,内存布局是不连续的,所以需要找到一种方案能将这些动态内存串联起来,这就要用到链表数据结构了。
3.0 链表原理
第一个链表节点保存的是第二个节点的地址,第二个节点保存的是第三个节点的地址,第三个节点保存的是下一个节点的地址。
4.0 创建链表节点
/*** @brief 创建链表节点* @param id 链表id编号* @param humi 温度数据* @param temp 湿度数据* */
typedef struct LINKNODE
{uint8_t id;uint8_t humi;uint8_t temp;struct LINKNODE *next;
} LinkNode_t;
注:为什么结构体中能嵌套自己,编译不会报错吗? 如果成员是struct TempHumiListNode next;会报错,因为编译器在解析这个成员时,结构体还没结束,不知道该为它分配多大内存空间;如果是*next,因为是指针类型,对于ARM 32平台,编译器会固定分配4个字节内存空间,用于保存下个子设备对应的动态内存首地址。
5.0 链表原理续
现在能将动态内存的数据串联起来了,但是还不够,需要设计一个“火车头”,火车头不需要对应实际的子设备,火车厢对应实际子设备,保存设备数据。
注:链表的火车头称之为头节点,火车厢叫做“节点”
“头节点”和“节点”只是动态内存块,在程序中还需要一个变量来保存头节点的首地址,这个变量就叫做“头指针”,准确的说应该是“头指针变量”,这样通过这个头指针就可以访问头节点数据,再通过头节点的next成员访问后面的节点:
6.0 链表实现
以下是实现链表的步骤:
1.0 链表初始化(创建头结点);
2.0 添加节点,当检测到新子设备时,添加到链表尾部;
3.0 遍历链表节点:
4.0 删除节点,当检测到子设备下线时,从链表中删除,
6.0.1 创建节点
typedef struct LINKNODE
{uint8_t id;uint8_t humi;uint8_t temp;struct LINKNODE *next;
} LinkNode_t;
注:节点中包含数据域和指针域,数据域包括id, humi, temp,指针域就是*next保存下一个节点的地址。
6.0.2 初始化链表
注:该操作的作用是创建头节点,用一个指针保存头节点的地址,方便头节点去访问后面的节点
LinkNode_t *InitLinkList(void)
{LinkNode_t *header = (LinkNode_t *)malloc(sizeof(LinkNode_t));if (header == NULL){return;}else{header->id = 0;header->next = NULL;return header;}
}
6.0.3 添加链表节点
出现新节点时,追加到链表的尾部,添加节点,尾节点的特点是NEXT成员保存的地址值为NULL
定义一个局部变量TempHumiListNode *current,从头节点开始循环遍历节点:
// 定义局部变量TempHumiListNode *current
LinkNode_t *current = header;
while (current->next != NULL)
{current = current->next;
}
添加节点
初始化赋值,TempHumiListNode *current = header,header==400,所以赋值以后current保存了地址400:
6.0.4 循环遍历
遍历链表节点,使得current从一个节点指向下一个节点,
循环边界,条件表达式:目标是找到next成员保存地址值为NULL的节点
遍历链表节点,使得current从一个节点指向下一个节点:要想current指向节点1,只需要将40C地址值赋值给current,这个地址值是保存在前向节点的next成员中,所以使用current->next可以获得40C地址值,接下来将它赋值给current就可以:current = current->next;
循环边界,条件表达式
目标是找到成员next保存地址值是NULL的节点就结束,所以循环条件表达式设计为
while (current->next != NULL)。
添加到新节点到链表尾部:current->next= node; node->next =NULL;
/*** @brief 模拟节点数据* @param 无参数* @return LinkNode_t**/
LinkNode_t *Search_LinkList_Data(void)
{LinkNode_t *node = (LinkNode_t *)malloc(sizeof(LinkNode_t));if (node == NULL){return NULL;}else{static uint8_t id = 100;id--;node->humi = 40;node->temp = 20.5f;return node;}
}
6.0.5 插入节点
static LinkNode_t *g_header;// 插入一个节点node
void Add_LinkList_Data(LinkNode_t *header, LinkNode_t *node)
{LinkNode_t *current = header;if (current->next != NULL){current = current->next;}current->next = node;node->next = NULL;
}
6.0.6 插入头结点main函数
int main(void)
{g_header = InitLinkList();if (g_header == NULL){return -1;}LinkNode_t *node;for (uint8_t i = 0; i < 3; i++){node = Search_LinkList_Data();if (node == NULL){continue;}Add_LinkList_Data(g_header, node);}return 0;
}
7.0 完整代码
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>/*** @brief 创建链表节点* @param id 链表id编号* @param humi 温度数据* @param temp 湿度数据**/
typedef struct LINKNODE
{uint8_t id;uint8_t humi;uint8_t temp;struct LINKNODE *next;
} LinkNode_t;/*** @brief 初始化链表* @param 无参数* @return LinkNode_t**/
LinkNode_t *InitLinkList(void)
{LinkNode_t *header = (LinkNode_t *)malloc(sizeof(LinkNode_t));if (header == NULL){return NULL;}else{header->id = 0;header->next = NULL;return header;}
}/*** @brief 模拟节点数据* @param 无参数* @return LinkNode_t**/
LinkNode_t *Search_LinkList_Data(void)
{LinkNode_t *node = (LinkNode_t *)malloc(sizeof(LinkNode_t));if (node == NULL){return NULL;}else{static uint8_t id = 100;id--;node->humi = 40;node->temp = 20.5f;return node;}
}static LinkNode_t *g_header;// 插入一个节点node
void Add_LinkList_Data(LinkNode_t *header, LinkNode_t *node)
{LinkNode_t *current = header;if (current->next != NULL){current = current->next;}current->next = node;node->next = NULL;
}int main(void)
{g_header = InitLinkList();if (g_header == NULL){return -1;}LinkNode_t *node;for (uint8_t i = 0; i < 3; i++){node = Search_LinkList_Data();if (node == NULL){continue;}Add_LinkList_Data(g_header, node);}return 0;
}
8.0 节点添加方案二
使用两个指针变量prev和current,prev,一直指向current的前向节点,初始化为:
TempHumiListNode *prev = g_header;TempHumiListNode *current = g_header->next;
8.0.1 循环遍历关键点
注:以下是使用这种方式的关键点
1: 遍历链表节点,使得prev和current从一个节点指向下一个节点
2: 循环边界,条件表达式:目标是通过prev找到next成员保存地址值为NULL的节点
8.0.2 添加节点的关键
步骤一
遍历链表节点,使得prev和current从一个节点指向下一个节点:
要想prev指向下一个节点,也就是current指向的节点,执行:prev=current;
要想current指向不一个节点,执行:current=current->next;
步骤二
循环边界,条件表达式;目标是通过prev找到next成员保存地址值为NULL的节点就结束:此时current指向为NULL,所以条件表达式设计为:while(current!= NULL)
第三步
添加到新节点到链表尾部
prev->next =node;node->next =NULL;
9.0 遍历链表节点
注:这样做的目的是打印所有节点的数据
使用一个指针变量current,参照添加节点第2种方案,从节点1开始循环遍历每一个节点,初始化
TempHumiListNode *currentcurrent = g header->next;
循环遍历关键点
遍历链表节点,使得current从一个节点指向下一个节点;
循环边界,条件表达式;目标是通过current遍历完所有节点
遍历链表节点,使得current从一个节点指向下一个节点:
要想current指向下一个节点,执行:current=current->next;
循环边界
循环边界,条件表达式;目标是通过current遍历完所有节点就结束:
此时current指向为NULL,所以条件表达式设计为:while(current!= NULL)
10.0 程序实现
注:以下是基于方案二代码的程序设计与实现,仅供学习参考
10.0.1 创建链表节点
相当于是链表里面的内存块,就是一个一个的节点,以下是具体的程序代码
// 创建链表节点
typedef struct LINKNODE
{uint8_t id;uint8_t humi;uint8_t temp;struct LINKNODE *next;
} LinkNode_t;
10.0.2 初始化链表
实际的初始化链表,表示的是设置链表的头部,也就是上面讲到的火车头,头指针保存的是头节点的地址。
实际代码程序:下面做实际的讲解
// 初始化链表
LinkNode_t *LinkNode_Init(void)
{// 创建结构体指针LinkNode_t *header = (LinkNode_t *)malloc(sizeof(LinkNode_t));if (header == NULL){printf("malloc has give space faild");return NULL;}else{header->id = 0;header->next = NULL;return header;}
}
LinkNode_t *LinkNode_Init(void) 是一个指针函数,函数的返回值是结构体类型,可以使用这个
LinkNode_t *header = (LinkNode_t *)malloc(sizeof(LinkNode_t));这段代码的意思是创建了一个结构体指针变量,给结构体指针变量分配内存地址
这段代码的意思是判断动态内存有没有分配成功,如果header == NULL表示没有分配成功,如果
header != NULL 表示动态内存开辟成功,这个时候else后面的代码就是给第一个内存块,也就是
链表的头部赋初始值,返回的第第一个节点(头节点)的地址。
10.0.3 模拟节点数据
注:这段代码的意思是,创建一个指针变量给它分配内存,如果分配失败返回NULL,分配成功写入一些固定的初始化值,返回节点的地址。
10.0.4 添加节点
注:这段代码的意思是创建一个添加节点的函数,函数的名字是LinkNode_Add,函数的参数是
(LinkNode_t *header, LinkNode_t *node),创建两个局部变量分别是:LinkNode_t *prev = header; LinkNode_t *pCurrent = header->next;用来保存头指针变量的地址和头指针变量下一个节点的地址。
这段代码的意思是,如果当前current指向的节点不是最后一个节点,那么prev和pCurrent分别向后移动一位,然后追加新的节点到链表的尾部,prev->next = node;其中prev保存的是下一个节点的地址,然后下一个节点的地址指向的是MULL。
10.0.5 打印节点
创建打印节点函数,参数是链表,创建一个指针指向头节点的下一个节点,判断内存分配有没有成功,如果没有成功直接返回,如果成工表示有节点,给节点赋值,然后当前的节点指向下一个节点,直到变量完最后一个节点为止。
10.0.6 main函数部分
g_header = LinkNode_Init(); 将结构体的地址赋值给全局变量,通过这个变量访问第一个头节点的地址,然后判断内存分配是否成功这个是基本的操作,成功打印节点,创建局部变量node
使用while循环里面嵌套了一个for循环次数是3次,然后给node赋值一个地址也就是节点的数据
判断内存分配是否成功不成功使用continue结束本次循环进入下一次循环,不是直接添加节点,打印输出。
.....删除节点......
相关文章:

1-1 C语言链表
目录 目录 1.0 定义 2.0 为什么使用链表 3.0 链表原理 4.0 创建链表节点 5.0 链表原理续 6.0 链表实现 6.0.1 创建节点 6.0.2 初始化链表 6.0.3 添加链表节点 6.0.4 循环遍历 6.0.5 插入节点 6.0.6 插入头结点main函数 7.0 完整代码 8.0 节点添加方案二 8.0.1 …...

[0629].第29节:配置中心业务规则与动态刷新
我的后端学习大纲 SpringCloud学习大纲 1、编码实现3377服务: 1.1.建module: 1.2.改pom: 1.3.写YML: 1.Nacos同Consul一样,在项目初始化时,要保证先从配置中心进行配置拉取,拉取配置之后,才能保证项目的正…...
mac: docker : Command not found解决
描述: 安装docker但是docker命令显示Command not found 分析: mac没有配置对应的环境变量 解决方案: 打开配置文件: vim ~/.zshrc写docker环境变量: export PATH"/Applications/Docker.app/Contents/Resources/bin:$PATH"保存退出: esc,输入wq,按enter 配置文…...

Django drf基于APIView 快速使用
1. 注册 # settings.pyINSTALLED_APPS [,rest_framework, ]2. 路由 from django.urls import pathurlpatterns [path(task/, views.TaskAPIView.as_view()) ]3. 视图 from rest_framework.views import APIView from rest_framework.response import Responseclass TaskAPIV…...
【MarsCode】每日一题数组 之 数字分组求偶数和
数字分组求偶数和 1.问题描述 问题描述 小M面对一组从 1 到 9 的数字,这些数字被分成多个小组,并从每个小组中选择一个数字组成一个新的数。目标是使得这个新数的各位数字之和为偶数。任务是计算出有多少种不同的分组和选择方法可以达到这一目标。 n…...
解决:error: subprocess-exited-with-error 的问题
系统和配置: ubuntu20.04 python3.10 torch2.5.1 pip install时报错如下 (实际指令是:pip3 install -r drl_grasping/python_requirements.txt) Collecting python-xlib>0.17 (from pynput1.7.6->-r drl_grasping/python_…...

使用腾讯混元(HunYuanVideo)视频模型FP8量化版本来生成绅士动画,模型体积30G,8G甜品卡可玩,2秒视频需要15分钟
腾讯混元(HunYuanVideo)视频模型发布以来,视频效果有口皆碑,但由于推理门槛比较高,消费级显卡用户望而却步,最近大神Kijai发布了FP8量化版本模型,使得甜品卡用户也有了一餐秀色的可能。 本次我们利用HunYuanVideo量化…...
使用Ancona安装node,安装vue
搜索Conda仓库中可用的Node.js版本 conda search nodejs 通过Conda安装Node.js conda install nodejs 检查已安装的Node.js版本 node -v 安装中国npm镜像(cnpm) conda install cnpm 使用cnpm全局安装Vue CLI cnpm install -g vue/cli...

如何“安装Android SDK“?
一、下载 https://android-sdk.en.softonic.com/ 二、解压(不能有中文) 三、配置环境变量 1、ANDROID_HOME:D:\android-sdk 2、在Path添加文件路径 四、验证 adb version...
天童教育:提升孩子的语言表达能力
语言表达能力如同阳光、空气和水,无处不在,无时不用。然而,很多人并没有意识到,想要让孩子能够良好适应社会生活,提升他们的语言表达能力是至关重要的。大连天童教育认为,我们务必重视孩子的语言表达能力&a…...
Node.js中JWT的token完整生命周期管理:从生成到销毁
Node.js中JWT的token完整生命周期管理:从生成到销毁 在Node.js中使用JWT(JSON Web Token)进行身份验证和授权是一种常见的实践。下面详细介绍JWT从生成到销毁的过程。 JWT生成 安装jsonwebtoken库: 要生成JWT,首先…...
Kotlin报错:lateinit property xxx has not been initialized
Kotlin报错:lateinit property xxx has not been initialized 发生在定义了一个名为xxx的lateinit变量。 解决,在调用前,可以先判断一层该xxx变量是否已经初始化: if (this::xxx.isInitialized) {//正常使用该变量} kotlin.Unini…...

debian编译失败
A、缘由和分析 debian的代码在删除该路径下的2个包后, 重新全编,编译不过的问题。 至于我为什么删除这2个包,这是因为在sdk第一次编译时一些文件已经打包进去了,我现在的修改无法更新进img中,而现在我的项目中不需要…...
flink-connector-mysql-cdc:03 mysql-cdc常见问题汇总
flink-connector-mysql-cdc: 01 mysql-cdc基础配置代码演示02 mysql-cdc高级扩展03 mysql-cdc常见问题汇总04 mysql-cdc-kafka生产级代码分享05 flink-kafka-doris生产级代码分享06 flink-kafka-hudi生产级代码分享flink-cdc版本:3.2.0 flink版本:flink-1.18.0 mysql版本:…...

JSP技术发展现状
多年前,Java入门时学习的JSP可谓时风光无限,J2EE如日中天,短短数年,技术迭代更新光速般发展,有些技术慢慢就退出历史舞台。 JSP(Java Server Pages) 技术在早期 Java Web 开发中曾是构建动态网…...
浏览器同源策略、跨域、跨域请求,服务器处理没、跨域解决方案
目录 什么是同源策略什么是跨域发生跨域时,服务器有没有接到请求并处理响应:(两种情况) 如何解决跨域 什么是同源策略 概念: 同源策略是浏览器的一种安全机制,用于防止恶意网站对用户的敏感数据进行未经授…...
flink-connector-mysql-cdc:02 mysql-cdc高级扩展
flink-connector-mysql-cdc:01 mysql-cdc基础配置代码演示02 mysql-cdc高级扩展03 mysql-cdc常见问题汇总04 mysql-cdc-kafka生产级代码分享05 flink-kafka-doris生产级代码分享06 flink-kafka-hudi生产级代码分享 flink-cdc版本:3.2.0flink版本…...
Couchbase 简介
Couchbase 是一款分布式 NoSQL 数据库,主要用于现代应用程序中高性能、高可扩展性和灵活的数据存储需求。它结合了文档存储和键值存储的特性,为开发者提供了一种高效的数据库解决方案。 Couchbase 的特点 高性能: 支持内存优先的架构&#x…...

我们来学mysql -- 事务并发之幻读(原理篇)
事务并发之幻读 题记幻读系列文章 题记 在《事务之概念》提到事务对应现实世界的状态转换,这个过程要满足4个特性这世界,真理只在大炮射程之类,通往和平的道路,非“常人”可以驾驭一个人生活按部就班,人多起来&#x…...

Ubuntu Linux 图形界面工具管理磁盘分区和文件系统(八)
本文为Ubuntu Linux操作系统- 第八弹~~ 今天接着上文的内容,讲Linux磁盘分区存储的相关知识~ 上期回顾:命令行-管理磁盘分区和文件系统 今天看酷酷的雪獒铠甲!!雪獒铠甲合体~ 文章目录 磁盘管理器GNOME Disks主要功能安装命令 磁盘…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...
【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统
目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索(基于物理空间 广播范围)2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看
文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...

Ubuntu Cursor升级成v1.0
0. 当前版本低 使用当前 Cursor v0.50时 GitHub Copilot Chat 打不开,快捷键也不好用,当看到 Cursor 升级后,还是蛮高兴的 1. 下载 Cursor 下载地址:https://www.cursor.com/cn/downloads 点击下载 Linux (x64) ,…...