redis实现的双向链表还是比较容易看得懂的,其实现的原理很经典, 代码很整洁清晰。 以下是对其源码注释的翻译及本人见解的部分说明,如有偏颇欢迎指正: /* adlist.h - 通用双向链表的实现*/#ifndef __adlist_h__#define __adlist_h__/* 目前的数据结构只使用
redis实现的双向链表还是比较容易看得懂的,其实现的原理很经典, 代码很整洁清晰。
以下是对其源码注释的翻译及本人见解的部分说明,如有偏颇欢迎指正:
/* adlist.h - 通用双向链表的实现*/#ifndef __adlist_h__#define __adlist_h__/* 目前的数据结构只使用了node, list, and iterator. *//* list节点*/typedef struct listnode { struct listnode *prev; // 前向指针 struct listnode *next; // 后向指针 void *value; // 当前节点值} listnode;/* list迭代器*/typedef struct listiter { listnode *next; // 节点指针 int direction; // 迭代方向 } listiter;/*链表结构*/typedef struct list { listnode *head; // 头结点 listnode *tail; // 尾节点 void *(*dup)(void *ptr); // 复制函数 void (*free)(void *ptr); // 释放函数 int (*match)(void *ptr, void *key); // 匹对函数 unsigned long len; // 节点数量} list;/* 函数宏定义 */#define listlength(l) ((l)->len) // 链表长度#define listfirst(l) ((l)->head) // 链表头节点#define listlast(l) ((l)->tail) // 链表末节点#define listprevnode(n) ((n)->prev) // 指定节点的前驱节点#define listnextnode(n) ((n)->next) // 指定节点的后继节点#define listnodevalue(n) ((n)->value) // 指定节点的值/* 函数指针, 设置外部调用模块的自定义的方法 */#define listsetdupmethod(l,m) ((l)->dup = (m)) // 复制链表#define listsetfreemethod(l,m) ((l)->free = (m)) // 释放链表#define listsetmatchmethod(l,m) ((l)->match = (m)) // 匹配/* 函数指针, 获取外部调用模块的自定义的方法 */#define listgetdupmethod(l) ((l)->dup) // 获取复制的自定义方法#define listgetfree(l) ((l)->free) // 获取释放的自定义方法#define listgetmatchmethod(l) ((l)->match) // 获取匹配的自定义方法/* 函数原型 */list *listcreate(void); // 创建链表void listrelease(list *list); // 释放链表list *listaddnodehead(list *list, void *value); // 在表头添加节点list *listaddnodetail(list *list, void *value); // 在表尾添加节点list *listinsertnode(list *list, listnode *old_node, void *value, int after); // 在指定位置之后添加节点void listdelnode(list *list, listnode *node); // 删除节点listiter *listgetiterator(list *list, int direction); // 获取列表迭代器listnode *listnext(listiter *iter); // 获取下一个节点void listreleaseiterator(listiter *iter); // 释放列表迭代器list *listdup(list *orig); // 复制链表listnode *listsearchkey(list *list, void *key); // 给定key查找节点listnode *listindex(list *list, long index); // 给定index查找节点void listrewind(list *list, listiter *li); // 迭代器指针重新指向头节点void listrewindtail(list *list, listiter *li); // 迭代器指针重新指向末节点void listrotate(list *list); // 链表翻转, 末节点移动成为头节点/* 迭代器的迭代方向 */#define al_start_head 0#define al_start_tail 1#endif /* __adlist_h__ */
/* adlist.c - 通用双向链表的实现 */#include #include adlist.h#include zmalloc.h/* 创建新链表. 新建的链表可以用函数* alfreelist()来释放, 但调用此函数之前需要要应用手动释放对每个节点的私有值空间** 出现错误则返回null,否则返回指向该list的指针*/list *listcreate(void){ struct list *list; if ((list = zmalloc(sizeof(*list))) == null) // 用了在malloc之上封装的zmalloc来申请内存 return null; list->head = list->tail = null; list->len = 0; list->dup = null; list->free = null; list->match = null; return list;}/* 释放链表,该方法不能失败.* */void listrelease(list *list){ unsigned long len; listnode *current, *next; current = list->head; len = list->len; while(len--) { next = current->next; if (list->free) list->free(current->value); // 每个节点指向的空间都会被释放 zfree(current); // zfree基于系统函数free上的封装 current = next; } zfree(list);}/* 把包含指针指向的值的节点插入链表头部** 如发生错误,将返回null并且不对链表进行任何操作,** 如成功则返回该链表指针.*/list *listaddnodehead(list *list, void *value){ listnode *node; if ((node = zmalloc(sizeof(*node))) == null) return null; node->value = value; if (list->len == 0) { list->head = list->tail = node; node->prev = node->next = null; } else { node->prev = null; node->next = list->head; list->head->prev = node; list->head = node; } list->len++; return list;}/* 把包含指针指向的值的节点插入链表尾部,* 如发生错误,将返回null并且不对链表进行任何操作,** 如成功则返回该链表指针.*/list *listaddnodetail(list *list, void *value){ listnode *node; if ((node = zmalloc(sizeof(*node))) == null) return null; node->value = value; if (list->len == 0) { list->head = list->tail = node; node->prev = node->next = null; } else { node->prev = list->tail; node->next = null; list->tail->next = node; list->tail = node; } list->len++; return list;}/* 把新节点插入链表某个节点的前或后,* 如发生错误,将返回null并且不对链表进行任何操作,** 如成功则返回该链表指针.*/list *listinsertnode(list *list, listnode *old_node, void *value, int after) { listnode *node; if ((node = zmalloc(sizeof(*node))) == null) return null; node->value = value; if (after) { // after!=0则表示节点插入的位置在旧节点之后,否则在其之前 node->prev = old_node; node->next = old_node->next; if (list->tail == old_node) { list->tail = node; } } else { node->next = old_node; node->prev = old_node->prev; if (list->head == old_node) { list->head = node; } } if (node->prev != null) { node->prev->next = node; } if (node->next != null) { node->next->prev = node; } list->len++; return list;}/* 从链表中删除某个节点.* 会调用底层函数把节点的空间释放.** 该方法不能失败. */void listdelnode(list *list, listnode *node){ if (node->prev) node->prev->next = node->next; else list->head = node->next; if (node->next) node->next->prev = node->prev; else list->tail = node->prev; if (list->free) list->free(node->value); zfree(node); list->len--;}/* 获取迭代器对象'iter'. 在初始化之后*每次调用listnext()都会返回链表的下一个元素.** 该方法不能失败. */listiter *listgetiterator(list *list, int direction){ listiter *iter; if ((iter = zmalloc(sizeof(*iter))) == null) return null; if (direction == al_start_head) iter->next = list->head; else iter->next = list->tail; iter->direction = direction; return iter;}/* 释放迭代器对象的空间 */void listreleaseiterator(listiter *iter) { zfree(iter);}/* 将迭代器指针重新指向表头 */void listrewind(list *list, listiter *li) { li->next = list->head; li->direction = al_start_head;}/* 将迭代器指针重新指向表尾 */void listrewindtail(list *list, listiter *li){ li->next = list->tail; li->direction = al_start_tail;}/* 获取迭代器的下一个元素.* 可以通过listdelnode()方法来删除当前返回的节点,但不能删除其他的节点。** 如果成功则返回迭代器的下一个元素,否则返回null;* 因此推荐以下这样使用:** iter = listgetiterator(list,);* while ((node = listnext(iter)) != null) {* dosomethingwith(listnodevalue(node));* }** */listnode *listnext(listiter *iter){ listnode *current = iter->next; if (current != null) { if (iter->direction == al_start_head) iter->next = current->next; else iter->next = current->prev; } return current;}/* 复制整个链表. * 成功则返回复制的链表指针,否则返回null.** 复制的方法通过listsetdupmethod()来指定,* 如果没有指定dup方法则会完整拷贝原始节点的值.** 原始链表不会给更改. */list *listdup(list *orig) // 有个疑问: 既然需要保持原始链表的不被修改,为什么不加const修饰?{ list *copy; listiter *iter; listnode *node; if ((copy = listcreate()) == null) return null; copy->dup = orig->dup; copy->free = orig->free; copy->match = orig->match; iter = listgetiterator(orig, al_start_head); while((node = listnext(iter)) != null) { void *value; if (copy->dup) { value = copy->dup(node->value); if (value == null) { listrelease(copy); listreleaseiterator(iter); return null; } } else value = node->value; if (listaddnodetail(copy, value) == null) { listrelease(copy); listreleaseiterator(iter); return null; } } listreleaseiterator(iter); return copy;}/* 通过指定key来查找节点.* 查找节点的匹配方法可以通过listsetmatchmethod()来指定. * 如果外部调用模块没有指定匹配方法, 则直接比较key值和链表中节点指针指向的值.** 如果正常将返回第一个匹配的节点指针,如果找不到匹配元素则返回null. */listnode *listsearchkey(list *list, void *key){ listiter *iter; listnode *node; iter = listgetiterator(list, al_start_head); while((node = listnext(iter)) != null) { if (list->match) { // 使用自定义的match方法 if (list->match(node->value, key)) { listreleaseiterator(iter); return node; } } else { // 直接比较值 if (key == node->value) { listreleaseiterator(iter); return node; } } } listreleaseiterator(iter); // 释放iter对象 return null;}/* 根据index来获取元素。* 如果传入index为非负值,说明为正向迭代: 0为头节点,1为下一个节点,以此类推.* 如果为负值,则说明为反向迭代: -1为尾节点, -2为倒数第二个节点, 以此类推* 如果index越界则返回null. */listnode *listindex(list *list, long index) { listnode *n; if (index tail; while(index-- && n) n = n->prev; } else { n = list->head; while(index-- && n) n = n->next; } return n;}/* 翻转链表, 将尾节点插入到链表头. */void listrotate(list *list) { listnode *tail = list->tail; if (listlength(list) tail = tail->prev; list->tail->next = null; /* 将末节点插入链表头 */ list->head->prev = tail; tail->prev = null; tail->next = list->head; list->head = tail;}
有两点还需要继续了解:1)既然源码中list空间的创建及销毁是通过zmalloc模块的zmalloc和zfree来完成, zmalloc又是怎么实现的呢?
2)很好奇这么多对象指针都没有const作为限制, 是什么原因可以省略了它呢?