OneOS操作系统入门-06:链表

一、链表简介

  链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。通过链表项与链表项的首尾相连组成链表项,链表项中可以包含一个指向下一个链表项的指针而不包含指向上一个链表的指针(单向链表),也可以即包含一个指向下一个链表的指针又包含一个指向上一个链表的指针(双向链表)。在 OneOS 系统中链表用列表(list)表示,链表项用节点(node)表示。

1.1、单向链表简介

  OneOS 操作系统中的单向链表包含一个节点指针,这个节点指针指向下一个节点。OneOS操作系统中的单向链表是一个非循环链表,单向链表本身首尾并非相连,就是说单向链表中的最后一个节点的指向下一个节点的节点指针不指向单向链表中的第一个节点,而是指向了OS_NULL。单向链表示意图如下图所示:

1.1.1、单向链表链表项首部插入

  OneOS 操作系统中使用函数 os_slist_add()进行将单向链表项插入到单向链表首部的操作,函数 os_slist_add()的操作过程如下图所示 :

  函数 os_slist_add()用于将单向链表项 entry 添加到单向链表头 head 所在的单向链表的首部。接下来结合上图分析函数 os_slist_add()的操作过程。首先,将entry 中指向下一个节点的节点指针指向 head 中指向下一个节点的节点指针所指向的节点,即把①连接起来。然后再将 head 中指向下一个节点的节点指针指向 entry,即把②断开,再把③连接起来。这样就达到了将单向链表项 entry 添加到单向链表头所在的单向链表的首部的目的。

1.1.2、单向链表链表项尾部插入

OneOS 操作系统中使用函数 os_slist_add_tail()进行将单向链表项插入到单向链表尾部的操作。函数 os_slist_add_tail()的操作过程如下图所示:

函数os_slist_add_tail()用于将单向链表项entry添加到单向链表头head所在的单向链表的尾部。接下来通过上图分析函数 os_slist_add_tail()的操作过程。函数os_slist_add_tail()首先遍历整个单向链表,找到单向链表的最后一个单向链表项,如图中的单向链表项 node_n,然后接下来即进行与函数 os_slist_add()类似的操作,首先将 node_n 中指向下一个节点的节点指针指向 entry,即把①断开,再把②连接起来。再将 entry 中指向下一个节点的节点指针指向 OS_NULL,即把③连接起来。这样就达到了将单向链表项 entry 添加到单向链表头所在的单向链表的尾部的目的。

1.1.3、单向链表的链表项删除

OneOS操作系统中使用函数os_slist_del()进行将单向链表项从单向链表中删除的操作。函数 os_slist_del()的操作过程如下图所示:

  函数 os_slist_del()用于将单向链表项entry 从单向链表头 head 所在的单向链表中删除。接下来结合上图分析函数 os_slist_del()的操作过程。函数 os_slist_del()首先遍历整个单向链表,找到指向单向链表项 entry 的节点指针所在的单向链表项,如图中的单向链表项 node_n,如果遍历整个链表都没有找到单向链表项entry则退出函数。然后将 node_n 中原本指向 entry 的指向下一个节点的节点指针指向entry中指向下一个节点的节点指针所指向的单向链表项 node_m,即把①断开,再把②连接起来。都这样就达到了将单向链表项 entry 从单向链表头head 所在的单向链表中删除的目的。

1.2、双向链表简介

OneOS 操作系统中的双向链表包含了两个结点指针,一个节点指针指向下一个节点,另一个节点指针指向上一个节点。OneOS 操作系统中的双向链表是一个循环链表,双向链表本身首尾相连,就是说双向链表中的最后一个节点的指向下一个节点的节点指针指向第一个节点,第一个节点的指向上一个节点的节点指针指向最后一个节点。双向链表的示意图如下图所示:

1.2.1、双向链表的链表项首部插入

OneOS 操作系统中使用函数 os_list_add()进行将双向链表项插入到双向链表首部的操作,函数 os_list_add()的操作过程如下图所示:

函数 os_list_add()用于将双向链表项 entry 添加到双向链表头 head 所在的双向链表的首部。接下来结合上图分析函数 os_list_add()的操作过程。首先将entey 中指向下一个节点的节点指针所指向的节点中指向上一个节点的节点指针指向 entry,即把①断开,再把②连接起来。然后将entry 中指向下一个节点的节点指针指向 head中指向下一个节点的节点指针所指向的节点,即将③连接起来。其次将head 中指向下一个节点的节点指针指向 entry,即将④断开,再将⑤连接起来。最后把entry中指向上一个节点的节点指针指向 head,即将⑥连接起来。这样就达到了将双向链表项 entry添加到双向链表头 head 所在的双向链表的首部的目的。

1.2.2、双向链表的链表项尾部插入

OneOS 操作系统中使用函数 os_list_add_tail()进行将双向链表项插入到双向链表尾部的操作。函数 os_list_add_tail()的操作过程如下图所示:

函数 os_list_add_tail()用于将双向链表项 entry 添加到双向链表头 head 所在的双向链表的尾部。接下来结合上图分析函数 os_list_add_tail()的操作过程。函数将双向链表头 head 中指向上一个节点的节点指针所指向的节点中指向下一个节点的结点指针指向 entry,即把①断开,再把②连接起来。然后将 entry 中指向上一个节点的节点指针指向 head 中指向上一个节点的结点指针所指向的节点,即把③连接起来。再将 head 中指向上一个节点的节点指针指向 entry,即把④断开,再把⑤连接起来。最后将 entry 中指向下一个节点的节点指针指向 head,即把⑥连接起来。这样就达到了将双向链表项 entry 添加到双向链表头 head 所在的双向链表的尾部的目的。

1.2.3、双向链表的链表项尾部删除(同单项链表类似,不做讲解)

二、单项链表实验

代码解释如下:

先需要定义任务  同静态创建任务一致,定义任务的优先级、堆栈大小、任务控制块、任务名称和任务函数 。其次定义要插入链表的项,然后定义链表信息结构体 包括链表的节点、索引和数值

任务函数 slist_task 是这段代码的核心部分,它演示了如何在操作系统中使用单向链表(slist)。
以下是对 slist_task 函数中关键代码段的详细解释:

(1)定义了一个静态任务函数slist_task,并且声明、初始化了链表的头节点slist_head
声明了指向 slist_info结构体的指针*data,这个结构体用于存储链表节点的数据。
声明了一个指向 os_slist_node_t 类型的指针 node_temp。这个指针将用于安全遍历链表,允许在遍历过程中删除节点。
声明了一个指向 os_slist_node_t 类型的指针 node,用于遍历链表。
定义了 变量 i,它将用作循环计数器
调用 os_slist_empty 函数检查链表是否为空。如果链表头节点的特定字段表明链表为空,函数返回非零值。
如果是空的,则执行下面的函数进行链表项的插入和删除。

(2)链表插入操作在for循环中进行
在循环体内,调用 os_malloc 函数为新的 slist_info_t 结构体分配内存。返回的内存块地址被赋给指针 data。
然后将 slist_value 数组中索引为 i 的元素值赋给新分配结构体的 value 字段。
检查当前索引 i 是否小于 slist_num 的一半。如果是,使用 os_kprintf 函数打印一条消息到控制台,说明正在将节点插入到链表的前面,并显示节点的 id 和 value。否则,打印一条消息到控制台,说明正在将节点插入到链表的尾部,并显示节点的 id 和 value。
这个循环的目的是构建一个链表,其中前半部分节点被插入到链表头部,而后半部分节点被插入到链表尾部。
此处要插入的数组为{70, 50, 68,95,28},70,50插入到头部,68,95,28插入到尾部  所以最后的结果就是  50,70,68,95,28。
(3)遍历与删除操作
使用 os_slist_for_each 宏遍历链表,os_slist_entry 函数用于从链表节点中获取指向结构体的指针。然后打印每个节点的 id 和 value。
使用os_slist_for_each_safe 宏安全地遍历链表,允许在遍历过程中删除节点。如果节点的 value 小于60,则打印删除信息,
使用 os_slist_del 删除节点,并使用 os_free 释放节点占用的内存。
在删除特定节点后,再次遍历链表并打印剩余节点的信息,显示链表的当前状态。

slist_task 函数演示了链表的基本操作,包括初始化、添加节点、遍历、删除节点以及内存管理。通过这个函数,
我们可以看到如何在操作系统中使用动态内存和链表来存储和管理数据集合。此外,通过打印操作,我们可以观察到链表状态的变化,验证链表操作的正确性。

main 函数的作用是初始化和启动应用程序的主要任务。它通过调用操作系统的 API 创建一个任务,配置任务的名称、栈大小、优先级等属性,
然后将任务加入到就绪队列中,等待调度器的调度。一旦 os_task_startup 被调用,任务将开始执行其函数,即 slist_task,在本例中,
它将演示单向链表的操作。

#include <board.h>
#include <os_memory.h>
#include <string.h>

/* oneOS配置************************************************************************************** */
/* SLIST_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define SLIST_TASK_PRIO   5               /* 任务优先级 */
#define SLIST_STK_SIZE    512             /* 任务堆栈大小 */
os_task_dummy_t *SLIST_Handler; 
os_task_id taskk1;
/* 任务控制块 */
void slist_task(void *parameter);         /* 任务函数 */
/********************************************************************************************************/

#define SLIST_NUM        3

int slist_value[SLIST_NUM] = {70, 50, 68};

struct slist_info /* 链表信息结构体 */
{
    os_slist_node_t slist_node;/* 链表的节点 */
    int id;            /* 链表的索引 */
    int value;         /* 链表的数值 */
};
typedef struct slist_info slist_info_t;

/**
 * @brief       slist_task
 * @param       parameter : 传入参数(未用到)
 * @retval      无
 */
static void slist_task(void *parameter)
{
    parameter = parameter;
    os_slist_node_t slist_head;
    os_slist_init(&slist_head);
    slist_info_t *data;
    os_slist_node_t *node_temp;
    os_slist_node_t *node;
    int i;

    
    os_task_msleep(100) ;   /* 提高串口消息的整洁,等待内核的消息打印后,再打印消息 */
    
    if (os_slist_empty(&slist_head))
    {
        os_kprintf("slist is empty!!!\r\n");
    }
    
    for (i = 0; i < SLIST_NUM; i++)
    {
        data = os_malloc(sizeof(slist_info_t));
        data->id = i;
        data->value = slist_value[i];
        
        if (i < SLIST_NUM/2)
        {
            os_kprintf("insert front-- id:%d score:%d\r\n", data->id, data->value);
            os_slist_add(&slist_head, &data->slist_node);    /* 链表项添加到链表头部在头节点之后 */
        }
        else
        {
            os_kprintf("insert tail -- id:%d score:%d\r\n", data->id, data->value);
            os_slist_add_tail(&slist_head, &data->slist_node);/* 链表项添加到链表尾部 */
        }
    }
 
    os_slist_for_each(node, &slist_head) /* 遍例链表 */
    {
        data = os_slist_entry(node, slist_info_t, slist_node);
        os_kprintf("slist id:%d value:%d\r\n", data->id, data->value);
    }

    os_slist_for_each_safe(node, node_temp, &slist_head)        /* 安全遍例链表,同时获取链表项结构体指针 */
    {
        data = os_slist_entry(node, slist_info_t, slist_node);

        if (data->value < 60) /* 小于60删除链表项 */
        {
            os_kprintf("delete -- slist id:%d slist value:%d\r\n", data->id, data->value);
            os_slist_del(&slist_head, &data->slist_node);
            os_free(data);
        }
    }

    
    os_slist_for_each(node, &slist_head) /* 遍例链表 */
    {
        data = os_slist_entry(node, slist_info_t, slist_node);
        os_kprintf("slist id:%d value:%d\r\n", data->id, data->value);
    }

    while(1)
    {
        os_task_msleep(10) ;
    }
}

int main(void)
{
    taskk1 = os_task_create(SLIST_Handler,
	                                 OS_NULL,
	                                 SLIST_STK_SIZE,
	                                "slist_task",            /* 设置任务的名称 */
                                   slist_task,              /* 设置任务函数 */
                                   OS_NULL,                 /* 任务传入的参数 */                                          /* 设置任务堆栈 */
                                   SLIST_TASK_PRIO);        /* 设置任务的优先级 */
    OS_ASSERT(taskk1);
    os_task_startup(taskk1);                         /* 任务开始 */
  
    return 0;
}

  • 13
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值