libevent中这样定义链表的好处.
我最近在看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也是差不多这样定义的。