对于dllist.h的分析
引言
该文件是典型的C/C++头文件,dllist是doubly linked list的缩写,即双向链表。双向链表是一种常用的数据结构,每个元素(节点)都包含了指向前一个节点和后一个节点的指针,因此可以双向遍历链表。
代码
#ifndef DLLIST_H
#define DLLIST_H
struct Dllist;
struct Dlelem;
// Dlelem is a node in a doubly linked list.
typedef struct Dlelem {
struct Dlelem* dle_next; /* next element */
struct Dlelem* dle_prev; /* previous element */
void* dle_val; /* value of the element */
struct Dllist* dle_list; /* what list this element is in */
} Dlelem;
typedef struct Dllist {
Dlelem* dll_head; /* head of list */
Dlelem* dll_tail; /* tail of list */
uint64 dll_len; /* number of elements in list */
} Dllist;
class DllistWithLock : public BaseObject {
public:
DllistWithLock(); // constructor
~DllistWithLock(); // destructor
void Remove(Dlelem* e) {
(void)RemoveConfirm(e);
}
bool RemoveConfirm(Dlelem* e); // remove element from list, return true if removed
void AddHead(Dlelem* e); // add element to head of list
void AddTail(Dlelem* e); // add element to tail of list
Dlelem* RemoveHead(); // remove element from head of list
Dlelem* RemoveHeadNoLock(); // remove element from head of list without lock
Dlelem* RemoveTail(); // remove element from tail of list
bool IsEmpty(); // is the list empty?
Dlelem* GetHead(); // get the head of the list
void GetLock(); // get the lock
void ReleaseLock(); // release the lock
inline uint64 GetLength() { // get the length of the list
return m_list.dll_len;
}
private:
slock_t m_lock; // lock for the list
Dllist m_list; // the list
};
extern Dllist* DLNewList(void); /* allocate and initialize a list header */
extern void DLInitList(Dllist* list); /* init a header alloced by caller */
extern void DLFreeList(Dllist* list); /* free up a list and all the nodes in
* it */
extern Dlelem* DLNewElem(void* val); /* allocate a new list element */
extern void DLInitElem(Dlelem* e, void* val); /* initialize caller-allocated node */
extern void DLFreeElem(Dlelem* e); /* free a list element */
extern void DLRemove(Dlelem* e); /* removes node from list */
extern void DLAddHead(Dllist* list, Dlelem* node); /* add node to head of list */
extern void DLAddTail(Dllist* list, Dlelem* node); /* add node to tail of list */
extern Dlelem* DLRemHead(Dllist* list); /* remove and return the head */
extern Dlelem* DLRemTail(Dllist* list); /* remove and return the tail */
extern void DLMoveToFront(Dlelem* e); /* move node to front of its list */
extern uint64 DLListLength(Dllist* list);
/* These are macros for speed */
#define DLGetHead(list) ((list)->dll_head) /* get the head of the list */
#define DLGetTail(list) ((list)->dll_tail) /* get the tail of the list */
#define DLIsNIL(list) ((list)->dll_head == NULL) /* is the list empty? */
#define DLGetSucc(elem) ((elem)->dle_next) /* get the successor */
#define DLGetPred(elem) ((elem)->dle_prev) /* get the predecessor */
#define DLGetListHdr(elem) ((elem)->dle_list) /* get the list header */
#define DLE_VAL(elem) ((elem)->dle_val) /* get the value of the * element */
#endif /* DLLIST_H */
以下是代码的主要组成部分和功能:
-
结构体定义:
Dlelem
结构体定义了双向链表的节点(元素)结构。每个节点包括指向下一个节点和上一个节点的指针,以及一个void*
类型的指针,用于存储节点的值,还有就是属于哪一个Dllist。Dllist
结构体定义了双向链表的头部结构。它包含指向链表头和尾的指针,以及一个表示链表长度的整数。
-
类定义:
DllistWithLock
是一个类,它扩展了基类BaseObject
。这个类似乎是为了实现带锁的双向链表。它包括了一些基本的双向链表操作,如添加、删除、获取头节点等,同时提供了锁定和释放锁的方法,以确保线程安全性。
-
函数原型和宏定义:
DLNewList
:分配并初始化一个新的链表头。DLInitList
:初始化一个由调用者分配的链表头。DLFreeList
:释放链表及其所有节点。DLNewElem
:创建一个包含特定值的新节点。DLInitElem
:初始化一个由调用者分配的节点。DLFreeElem
:释放一个节点。DLRemove
:从链表中移除一个节点。DLAddHead
:将节点添加到链表的头部。DLAddTail
:将节点添加到链表的尾部。DLRemHead
:移除并返回链表头部的节点。DLRemTail
:移除并返回链表尾部的节点。DLMoveToFront
:将节点移动到其所在链表的头部。DLListLength
:返回链表的长度。
-
宏定义:
- 一些宏(macros)用于简化双向链表操作,包括获取链表头、获取链表尾、检查链表是否为空、获取节点的后继和前驱节点以及获取节点所在的链表头和节点值。
extern
关键字用于声明变量或函数的外部链接性,以告诉编译器在其他地方定义了这些变量或函数,但当前文件只需要知道它们的存在。这有助于编写模块化的代码,并将代码分解成多个文件,以便更好地组织和管理项目。
这个头文件提供了一组用于管理双向链表的基本操作,可以用于各种不同的应用中。具体的实现代码需要在其他文件中实现,通过包含这个头文件来使用这些操作。
示例
以下是双向链表的使用示例
// Here's a small example of how to use Dllists:
Dllist *lst;
Dlelem *elt;
void *in_stuff; // stuff to stick in the list
void *out_stuff
lst = DLNewList(); // make a new dllist
DLAddHead(lst, DLNewElem(in_stuff));
//add a new element to the list with in_stuff as the value
elt = DLGetHead(lst); // retrieve the head element
out_stuff = (void*)DLE_VAL(elt); // get the stuff out
DLRemove(elt); // removes the element from its list
DLFreeElem(elt);
// free the element since we don't use it anymore
这段代码演示了如何使用双向链表(Dllist)进行基本操作:
- 首先,声明了一个指向双向链表的指针
lst
和一个指向链表元素的指针elt
。 - 使用
DLNewList()
创建了一个新的双向链表,并将其地址分配给lst
。 - 使用
DLAddHead()
在链表的头部添加了一个新元素,该元素的值为in_stuff
。 - 使用
DLGetHead()
获取链表的头元素,并将其地址分配给elt
。 - 使用
DLE_VAL()
获取元素中的数据,然后将其存储在out_stuff
中。 - 使用
DLRemove()
从链表中移除元素,以确保元素不再属于链表。 - 最后,使用
DLFreeElem()
释放元素的内存,因为不再需要它。
示意图如下:
这个示例展示了如何创建、添加、检索、移除和释放双向链表中的元素。双向链表是一种常见的数据结构,用于在程序中组织和管理数据。
总结
双向链表(Doubly Linked List)是一种常见的线性数据结构,与普通链表(单向链表)相似,但每个节点除了指向下一个节点外,还指向前一个节点,因此可以在两个方向上遍历链表。
双向链表是一种灵活的数据结构,可以在两个方向上遍历,适用于需要频繁插入和删除节点的场景。然而,由于额外的指针开销,它可能占用更多的内存。在选择数据结构时,需要根据具体的需求来权衡其优势和劣势。
的线性数据结构,与普通链表(单向链表)相似,但每个节点除了指向下一个节点外,还指向前一个节点,因此可以在两个方向上遍历链表。
双向链表是一种灵活的数据结构,可以在两个方向上遍历,适用于需要频繁插入和删除节点的场景。然而,由于额外的指针开销,它可能占用更多的内存。在选择数据结构时,需要根据具体的需求来权衡其优势和劣势。
本文从dllist的头文件出发,了解了双向链表的基本构成和使用方法,此外在项目文件中,为了便于查找和维护链表,添加了诸多函数宏和extern外部函数声明,这种开发模式给我们在实际开发过程中给予重要的指导作用。