IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> liteOS中双向循环链表的一些设计思想 -> 正文阅读

[数据结构与算法]liteOS中双向循环链表的一些设计思想

1. 问题提出

看liteOS的源码,发现这些伙计们的水平还是高的。对于一个双向链表,一般我们的写法都是:

typedef struct Node {
	int data;
	struct Node *prev;
	struct Node *next;
} NODE

但是这就诞生了一个问题,节点Node中的数据类型是int时我们定义了一个链表类型NODE0,如果是float data又定义了一种链表类型NODE1,或者有多个业务变量时我们又定义了NODE2,这时候如果我们想写一个链表遍历函数,希望能够对所有的链表适用,怎么办呢?使用C++也许容易解决这个问题,模板嘛;但是C语言怎么办呢?

2. liteOS解决思路

liteOS提供了解决思路,把链表相关的单独剥离出来,如下所示,链表节点中什么业务数据都没有,只有前项节点指针和后项节点指针,显然,这时针对链表结构体的遍历、插入等操作就变得简单而广泛适用。

typedef struct LOS_DL_LIST {
    struct LOS_DL_LIST *pstPrev; /**< Current node's pointer to the previous node */
    struct LOS_DL_LIST *pstNext; /**< Current node's pointer to the next node */
} LOS_DL_LIST;

让我们简单看一个插入节点函数,这很容易理解,不做解释了。

LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListAdd(LOS_DL_LIST *list, LOS_DL_LIST *node)
{
    node->pstNext = list->pstNext;
    node->pstPrev = list;
    list->pstNext->pstPrev = node;
    list->pstNext = node;
}

当需要定义业务结构体时,则需要把上面定义的双向链表结构体作为成员变量,举个例子,下面是互斥锁结构体LosMuxCB定义,其中包含双向链表LOS_DL_LIST muxList作为成员变量:

typedef struct {
    LOS_DL_LIST muxList; /** 互斥锁的双向链表*/
    LosTaskCB *owner; /** 当前持有锁的任务TCB */
    UINT16 muxCount; /** 持有互斥锁的次数 */
    UINT8 muxStat; /** 互斥锁状态OS_MUX_UNUSED, OS_MUX_USED */
    UINT32 muxId; /** 互斥锁handler ID*/
} LosMuxCB;

在这里插入图片描述
新的问题就来了,虽然遍历、插入双向链表很容易了,但是我们在使用的时候肯定是对业务结构体进行遍历的啊,如何从链表遍历过渡到业务结构体的遍历呢?
问题的关键是计算出在一个业务结构体中,链表节点的地址相对于业务结构体起始地址的偏移,得到该偏移,我们就可以得到业务结构体指针,从而完成遍历。
业务结构体类型名称type、其中的双向链表成员变量名称member,下面这个宏定义就巧妙实现了求解链表节点地址在一个业务结构体中的地址偏移;(type *)0->member这一骚操作,很好,很喜欢。

#define LOS_OFF_SET_OF(type, member) ((UINTPTR)&((type *)0)->member)

下面这个宏定义更进一步,求出了业务结构体的实际内存地址,其中item是双向链表的实际内存地址。

#define LOS_DL_LIST_ENTRY(item, type, member) \
    ((type *)(VOID *)((CHAR *)(item) - LOS_OFF_SET_OF(type, member)))

再进一步,让我们开始愉快地遍历业务结构体吧,下面宏定义中,item代表逐个遍历的业务结构体地址,list代表双向链表指针,type和member同上。

#define LOS_DL_LIST_FOR_EACH_ENTRY(item, list, type, member)             \
    for (item = LOS_DL_LIST_ENTRY((list)->pstNext, type, member);        \
         &(item)->member != (list);                                      \
         item = LOS_DL_LIST_ENTRY((item)->member.pstNext, type, member))

到此,liteOS通过实现上述的宏定义/函数,实现了一套链表函数适用于所有的业务结构体,在C语言中写出了C++的风采,值得参考。

3. 参考

  1. liteOS源码:https://gitee.com/LiteOS/LiteOS/blob/master/kernel/include/los_list.h
  2. LiteOS内核源码分析系列一 盘点那些重要的数据结构 (1):https://bbs.huaweicloud.com/blogs/244926
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-03-06 13:22:01  更:2022-03-06 13:23:54 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/27 16:41:03-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码