1# 双向链表 2 3 4## 基本概念 5 6双向链表是指含有往前和往后两个方向的链表,即每个结点中除存放下一个节点指针外,还增加一个指向前一个节点的指针。其头指针head是唯一确定的。 7 8从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点,这种数据结构形式使得双向链表在查找时更加方便,特别是大量数据的遍历。由于双向链表具有对称性,能方便地完成各种插入、删除等操作,但需要注意前后方向的操作。 9 10 11## 功能说明 12 13双向链表模块为用户提供下面几种功能,接口详细信息可以查看API参考。 14 15| | | 16| -------- | -------- | 17| **功能分类** | **接口描述** | 18| 初始化和删除链表 | LOS_ListInit:将指定双向链表节点初始化为双向链表。<br/> LOS_DL_LIST_HEAD:定义一个双向链表节点并以该节点初始化为双向链表。<br/> LOS_ListDelInit:删除指定的双向链表。 | 19| 增加节点 | LOS_ListAdd:将指定节点插入到双向链表头端。<br/> LOS_ListTailInsert:将指定节点插入到双向链表尾端。 | 20| 删除节点 | LOS_ListDelete:将指定节点从链表中删除。<br/> LOS_ListDelInit:将指定节点从链表中删除,并使用该节点初始化链表。 | 21| 判断双向链表是否为空 | LOS_ListEmpty:判断链表是否为空。 | 22| 获取结构体信息 | LOS_DL_LIST_ENTRY:获取包含链表的结构体地址,接口的第一个入参表示的是链表中的某个节点,第二个入参是要获取的结构体名称,第三个入参是链表在该结构体中的名称。<br/> LOS_OFF_SET_OF:获取指定结构体内的成员相对于结构体起始地址的偏移量。 | 23| 遍历双向链表 | LOS_DL_LIST_FOR_EACH:遍历双向链表。<br/> LOS_DL_LIST_FOR_EACH_SAFE:遍历双向链表,并存储当前节点的后继节点用于安全校验。 | 24| 遍历包含双向链表的结构体 | LOS_DL_LIST_FOR_EACH_ENTRY:遍历指定双向链表,获取包含该链表节点的结构体地址。<br/> LOS_DL_LIST_FOR_EACH_ENTRY_SAFE:遍历指定双向链表,获取包含该链表节点的结构体地址,并存储包含当前节点的后继节点的结构体地址。 | 25 26 27## 开发流程 28 29双向链表的典型开发流程: 30 311. 调用LOS_ListInit/LOS_DL_LIST_HEAD初始双向链表。 32 332. 调用LOS_ListAdd向链表插入节点。 34 353. 调用LOS_ListTailInsert向链表尾部插入节点。 36 374. 调用LOS_ListDelete删除指定节点。 38 395. 调用LOS_ListEmpty判断链表是否为空。 40 416. 调用LOS_ListDelInit删除指定节点并以此节点初始化链表。 42 43 44> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:** 45> - 需要注意节点指针前后方向的操作。 46> 47> - 链表操作接口,为底层接口,不对入参进行判空,需要使用者确保传参合法。 48> 49> - 如果链表节点的内存是动态申请的,删除节点时,要注意释放内存。 50 51 52## 编程实例 53 54 55### 实例描述 56 57本实例实现如下功能: 58 591. 初始化双向链表。 60 612. 增加节点。 62 633. 删除节点。 64 654. 测试操作是否成功。 66 67 68### 示例代码 69 70示例代码如下: 71 72本演示代码在 ./kernel/liteos_m/testsuites/src/osTest.c 中编译验证,在TestTaskEntry中调用验证入口函数ExampleList。 73 74 75``` 76#include "stdio.h" 77#include "los_list.h" 78 79STATIC UINT32 ExampleList(VOID) 80{ 81 LOS_DL_LIST listHead = {NULL,NULL}; 82 LOS_DL_LIST listNode1 = {NULL,NULL}; 83 LOS_DL_LIST listNode2 = {NULL,NULL}; 84 85 /* 初始化链表 */ 86 printf("Initial head\n"); 87 LOS_ListInit(&listHead); 88 89 /* 添加节点1和节点2,并校验他们的相互关系 */ 90 LOS_ListAdd(&listHead, &listNode1); 91 if (listNode1.pstNext == &listHead && listNode1.pstPrev == &listHead) { 92 printf("Add listNode1 success\n"); 93 } 94 95 LOS_ListTailInsert(&listHead, &listNode2); 96 if (listNode2.pstNext == &listHead && listNode2.pstPrev == &listNode1) { 97 printf("Tail insert listNode2 success\n"); 98 } 99 100 /* 删除两个节点 */ 101 LOS_ListDelete(&listNode1); 102 LOS_ListDelete(&listNode2); 103 104 /* 确认链表为空 */ 105 if (LOS_ListEmpty(&listHead)) { 106 printf("Delete success\n"); 107 } 108 109 return LOS_OK; 110} 111``` 112 113 114### 结果验证 115 116编译运行得到的结果为: 117 118 119``` 120Initial head 121Add listNode1 success 122Tail insert listNode2 success 123Delete success 124``` 125