libevent中这样定义链表的好处.

fdl19881 2012-06-26 10:33:23
我最近在看libevent
其中他是这样定义链表的:
见libevent/compat/sys/queue.h
这样应该算作是改进的单链表了。如果非要用container_of宏的经过两次调用的话,也可以当作双链表。
140 #define LIST_HEAD(name, type) \
141 struct name { \
142 struct type *lh_first; /* first element */ \
143 }
144
145 #define LIST_HEAD_INITIALIZER(head) \
146 { NULL }
147
148 #define LIST_ENTRY(type) \
149 struct { \
150 struct type *le_next; /* next element */ \
151 struct type **le_prev; /* address of previous next element */ \
152 }

这种定义方法和我们常用的定义方法他的好处
为什么不这样定义呢?

140 #define LIST_HEAD(name, type) \
141 struct name { \
142 struct type *lh_first; /* first element */ \
143 }
144
145 #define LIST_HEAD_INITIALIZER(head) \
146 { NULL }
147
148 #define LIST_ENTRY(type) \
149 struct { \
150 struct type *le_next; /* next element */ \
151 struct type *le_prev; /* here */ \
152 }


我想了想, 前者定义方法的好处应该是:
插入的时候好使用方便。主要是插入或者删除头部指向的元素时方便,还能保留head的有效性。
比如这个是在元素前面插入时的宏
182 #define LIST_INSERT_BEFORE(listelm, elm, field) do { \
183 (elm)->field.le_prev = (listelm)->field.le_prev; \
184 (elm)->field.le_next = (listelm); \
185 *(listelm)->field.le_prev = (elm); \
186 (listelm)->field.le_prev = &(elm)->field.le_next; \
187 } while (0)

这样,即使链表中的listelm就是head->lh_first指向的,那么在插入后,head也会改变。因为first->lh_first->le_prev == &first->lh_first

但如果使用通常情况的双链表定义的话,head->lh_first是没法改变的! 因为没有无法得到指向head->lh_first的指针。而前者有。

在linux内核中struct hlist也是差不多这样定义的。

...全文
160 1 打赏 收藏 转发到动态 举报
写回复
用AI写文章
1 条回复
切换为时间正序
请发表友善的回复…
发表回复
qq120848369 2012-06-30
  • 打赏
  • 举报
回复
因为编程便利性的需求, 已经习惯让head=NULL代表链表为空了,并且是没有头结点的。
还有就是你贴的代码了,结点里存其他结点的next指针,目的就是免去复杂的判断逻辑,因为链表长度是1,2的时候真的判断的很累。

69,379

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧