当前位置:网站首页>RT-Thread 双向链表(学习笔记)
RT-Thread 双向链表(学习笔记)
2022-06-28 03:40:00 【小辉_Super】
本文参考自[野火EmbedFire]《RT-Thread内核实现与应用开发实战——基于STM32》,仅作为个人学习笔记。更详细的内容和步骤请查看原文(可到野火资料下载中心下载)
文章目录
双向链表的基本概念
双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表。
——百度百科
这种数据结构形式使得双向链表在查找时更加方便,特别是大量数据的遍历,由于双向链表具有对称性,能方便地完成各种插入、删除等操作,但需要注意前后方向的操作。
双向链表的函数接口
链表节点结构体
首先介绍 RT-Thread 中的链表节点结构体,这个和我们平时自己定义的双向链表没有什么区别。
/** * Double List structure */
struct rt_list_node
{
struct rt_list_node *next; /**< 尾指针,指向下一个节点. */
struct rt_list_node *prev; /**< 头指针,指向上一个节点. */
};
typedef struct rt_list_node rt_list_t; /**< Type for lists. */
链表初始化 rt_list_init()
链表使用之前必须初始化,将链表的头尾指针都指向自己。
/** * @brief initialize a list * * @param l list to be initialized */
rt_inline void rt_list_init(rt_list_t *l)
{
l->next = l->prev = l;
}

初始化函数里没有给链表头申请空间,这一步需要手动使用 rt_malloc() 来申请,具体可以参考下面的代码(参考原文)。
head = rt_malloc(sizeof(rt_list_t)); // 申请动态内存
if(RT_NULL == head)
rt_kprintf("动态内存申请失败!\n");
else
rt_kprintf("动态内存申请成功");
rt_kprintf("双向链表初始化中......\n");
rt_list_init(head);
if(rt_list_isempty(head)
rt_kprintf("双向链表初始化成功!\n");
向链表中指定节点后面插入节点 rt_list_insert_after()
插入节点前,需要先申请节点大小的内存(见下文),然后在指定节点后插入新的节点。
/** * @brief insert a node after a list * * @param l list to insert it * @param n new node to be inserted */
rt_inline void rt_list_insert_after(rt_list_t *l, rt_list_t *n)
{
l->next->prev = n;
n->next = l->next;
l->next = n;
n->prev = l;
}

向链表中指定节点前面插入节点 rt_list_insert_before()
插入节点前,需要先申请节点大小的内存,然后在指定节点前插入新的节点。
/** * @brief insert a node before a list * * @param n new node to be inserted * @param l list to insert it */
rt_inline void rt_list_insert_before(rt_list_t *l, rt_list_t *n)
{
l->prev->next = n;
n->prev = l->prev;
l->prev = n;
n->next = l;
}

下面是节点插入操作的示例代码(参考自原文):
// 动态申请第一个节点的内存
node1 = rt_malloc(sizeof(rt_list_t));
// 动态申请第二个节点的内存
node2 = rt_malloc(sizeof(rt_list_t));
// 将 node2 插入到 head 后面
rt_list_insert_after(head, node2);
// 将 node1 插入到 node2 前面
rt_list_insert_before(node2, node1);
if((node1->prev == head) && (node2->prev == node1))
rt_kprintf("添加节点成功!\n");
else
rt_kprintf("添加节点失败!\n");
从链表删除节点 rt_list_remove()
删除节点比添加节点简单,只需要将要删除节点前后两个节点连起来,再释放要删除节点的内存。
/** * @brief remove node from list. * @param n the node to remove from the list. */
rt_inline void rt_list_remove(rt_list_t *n)
{
n->next->prev = n->prev;
n->prev->next = n->next;
n->next = n->prev = n;
}

删除节点的具体方法:
// 原链表结构体:head ->> node1 ->> node2 ->> node3
rt_list_remove(node2);
rt_free(node2); // 释放 node2 的内存
if(node3->prev == node1) // 后面的节点是否与前面的节点相连
rt_kprintf("删除节点成功\n");
双向链表实验
#include "board.h"
#include "rtthread.h"
// 定义线程控制块指针
static rt_thread_t test_thread = RT_NULL;
/****************************************************************************** * @ 函数名 : test_thread_entry * @ 功 能 : 线程入口函数 * @ 参 数 : parameter 外部传入的参数 * @ 返回值 : 无 ******************************************************************************/
static void test_thread_entry(void *parameter)
{
rt_list_t *head; // 双向链表头
rt_list_t *node1; // 双向链表的节点1
rt_list_t *node2; // 双向链表的节点2
head = rt_malloc(sizeof(rt_list_t)); // 申请动态内存
if(RT_NULL == head) // 没有申请成功
rt_kprintf("动态内存申请失败!\n");
else
rt_kprintf("动态内存申请成功,头节点地址为%d !\n", head);
rt_kprintf("\n双向链表初始化中......\n");
rt_list_init(head);
if(rt_list_isempty(head))
rt_kprintf("双向链表初始化成功!\n");
//动态申请第一个节点的内存
node1 = rt_malloc(sizeof(rt_list_t));
// 动态申请第二个节点的内存
node2 = rt_malloc(sizeof(rt_list_t));
rt_kprintf("\n添加第一个节点与第二个节点......\n");
//分别插入 node2 和 node1,最终效果为 head ->> node1 ->> node2
rt_list_insert_after(head, node2);
rt_list_insert_before(node2, node1);
if((node1->prev == head) && (node2->prev == node1))
rt_kprintf("添加节点成功.\n");
else
rt_kprintf("添加节点失败.\n");
rt_kprintf("\n删除节点......\n");
rt_list_remove(node1);
rt_free(node1); // 释放第一个节点的内存
if(node2->prev == head)
rt_kprintf("删除节点成功.\n");
while(1)
{
LED0(ON);
rt_thread_delay(500); // 500个tick(500ms)
LED0(OFF);
rt_thread_delay(500);
}
}
int main(void)
{
// 硬件初始化和RTT的初始化已经在component.c中的rtthread_startup()完成
// 创建一个动态线程
test_thread = // 线程控制块指针
rt_thread_create("list_test", // 线程名字
test_thread_entry, // 线程入口函数
RT_NULL, // 入口函数参数
255, // 线程栈大小
5, // 线程优先级
10); // 线程时间片
// 开启线程调度
if(test_thread != RT_NULL)
rt_thread_startup(test_thread);
else
return -1;
}
实验现象

边栏推荐
- 04 MongoDB各种查询操作 以及聚合操作总结
- 从零到一,教你搭建「以文搜图」搜索服务(一)
- MSc 307 (88) (2010 FTPC code) Part 2 smoke and toxicity test
- Conversion between decimal and BCD codes in C language
- 月赛补题
- English notes - cause and effect
- 光伏板怎么申请ASTM E108阻燃测试?
- Two methods of shell script parameter passing based on arm5718
- 01 overview, application scenarios, Download methods, connection methods and development history of mongodb
- 第一章 Bash 入门
猜你喜欢

Chapter 1 Introduction to bash

从零到一,教你搭建「以文搜图」搜索服务(一)

开关电源—Buck电路原理及其仿真
![[graduation season] graduate summary](/img/f6/59134c1dbf70fc809652d925fd528f.jpg)
[graduation season] graduate summary

光的粒子说(光电效应/康普顿效应)

软件测试报告怎么编写?第三方性能报告范文模板来了

Reading notes of top performance version 2 (II) -- CPU monitoring

MySQL master-slave replication, separation and resolution

Market competitiveness of robot programming education

机器学习入门笔记
随机推荐
leetcode:494.数组中添加加减运算符得到指定值的所有方法
Uncertainty principle
Reading notes of top performance version 2 (II) -- CPU monitoring
Elk builds log analysis system + Zipkin service link tracking integration
【小程序实战系列】电商平台源码及功能实现
等保2.0密码要求是什么?法律依据有哪些?
gcd最大公约数
2021年终总结及2022年展望
Zipkin service link tracking
几个重要的物理概念
MySQL master-slave replication, separation and resolution
机器学习入门笔记
applicationContext. Getbeansoftype obtains the execution methods of all implementation classes under an interface or obtains the operation application scenarios such as implementation class objects. L
揭开SSL的神秘面纱,了解如何用SSL保护数据
Door level modeling - learning notes
Arrangement of basic electrical knowledge (I)
[MySQL] multi table connection query
GenICam GenTL 标准 ver1.5(2)
@Transactional失效的几种场景
django. core. exceptions. ImproperlyConfigured: mysqlclient 1.3.13 or newer is required; you have 0.9.3