链表在libnet中的实现
list链表是双链表,在libnet中的实现同样简单,其借用了linux中list的实现思路,将指针域与数据域分离。要用list也是跟list中一样的。
如下是指针域的实现代码——为了方便阅读,我用linux下的风格将语句分开了。
/* Linked list code, inspired by Links, but written from scratch. */
struct list_head {
void *next;
void *prev;
};
#define init_list(L) \
do { \
L.next = L.prev = &L; \
} while (0)
#define list_empty(L) (L.next == &L)
#define add_to_list(L, n) \
do { \
n->next = L.next; \
n->next->prev = n; \
n->prev = (void *) &L; \
L.next = n; \
} while (0)
#define append_to_list(L, n) \
do { \
n->prev = L.prev; \
n->prev->next = n; \
n->next = (void *) &L; \
L.prev = n; \
} while (0)
#define del_from_list(n) \
do { \
n->prev->next = n->next; \
n->next->prev = n->prev; \
} while (0)
#define foreach(n, L) \
for (n = L.next; \
n != (void *) &L; \
n = n->next) \
#define free_list(L, dtor) \
do { \
struct list_head *x, *y; \
for (x = L.next; \
x != (void *) &L; \
x = y) \
{ \
y = x->next; \
dtor ((void *) x); \
} \
init_list (L); \
} while (0)
代码没有任何注释,但是也还算清晰,有几点需要注意的
1)#define MACRO_NAME(para) do{macro content}while(0)
参考文章 《在宏定义中使用 do...while》
此处的用处显然是第三种
/*使用do{….}while(0) 把它包裹起来,成为一个独立的语法单元,从而不会与上下文发生混淆。
同时因为绝大多数的编译器都能够识别do{…}while(0)这种无用的循环并进行优化,所以使用这种
方法也不会导致程序的性能降低。*/
3)链表未循环双链表,但是插入只能在头和尾操作(对应add和append);删除将自己从链表中剥离(不涉及到内存的释放),该节点可以是链表任意节点。
4)释放链表传递了一个dtor函数,这要求用户自己实现——但是其没有提供检查机制,非常危险。
最后不推荐此种方式,关于宏的弊病此处不做讨论。