通用数据结构

本文介绍了Linux内核中的一系列通用数据结构,包括单链表、双向链表、队列等。这些数据结构在C语言项目中尤其有用,通常需要手动实现。Linux内核的这些实现经过验证,可以放心使用。文章特别强调了尾巴队列(tail queue)的使用,并提供了相关代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

几乎对于所有的程序来说不能缺少数据结构这个角色,不管是常用的单链表、双向链表还是队列,还是也不时碰到的堆、哈希表等等。如果这些这些东西在项目是经常使用到的话,那么如果只开发一次,再所有的项目都能使用上的话,是不是可以节省下来很多时间呢?当然,对于一些解析性的语言来说,这些都是包含在自己的语言库里,这帮了我们不少的忙。但是对于C语言来说,要想使用这些就得自己去实现,如果要想开发一套在所有项目中都能通用的就更难了。幸好,linux的一些大牛们帮我们开发了一套通用的数据结构,这些代码存在于linux内核源码中,至少能说明这些代码是没有问题的,可以放心使用。这些通用的代码包括:单向链表、双向链表、队列、尾巴队列(tail queue)、环形队列(Circular queue)。

最开始接触了解到这个也是在阅读libevent源码时看到的,不过其中也就使用到了tail queue这个东西。我也并不是一开始就觉得这些代码很优秀,在结合这些代码到自己的项目中时才发现有这些东西处理起来简单了很多。目前我使用过的也就是tail queue。下面把这些都贴出来,并详细说明下tail queue

#ifndef _SYS_QUEUE_H_

#define _SYS_QUEUE_H_

/*

 * Singly-linked List definitions.

 */

#define SLIST_HEAD(name, type) \

struct name { \

struct type *slh_first; /* first element */ \

}

#define SLIST_HEAD_INITIALIZER(head) \

{ NULL }

#ifndef WIN32

#define SLIST_ENTRY(type) \

struct { \

struct type *sle_next; /* next element */ \

}

#endif

/*

 * Singly-linked List access methods.µ¥ÏòÁ´±í

 */

#define SLIST_FIRST(head) ((head)->slh_first)

#define SLIST_END(head) NULL

#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))

#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)

#define SLIST_FOREACH(var, head, field) \

for((var) = SLIST_FIRST(head); \

    (var) != SLIST_END(head); \

    (var) = SLIST_NEXT(var, field))

/*

 * Singly-linked List functions.

 */

#define SLIST_INIT(head) { \

SLIST_FIRST(head) = SLIST_END(head); \

}

#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \

(elm)->field.sle_next = (slistelm)->field.sle_next; \

(slistelm)->field.sle_next = (elm); \

} while (0)

#define SLIST_INSERT_HEAD(head, elm, field) do { \

(elm)->field.sle_next = (head)->slh_first; \

(head)->slh_first = (elm); \

} while (0)

#define SLIST_REMOVE_HEAD(head, field) do { \

(head)->slh_first = (head)->slh_first->field.sle_next; \

} while (0)

/*

 * List definitions.

 */

#define LIST_HEAD(name, type) \

struct name { \

struct type *lh_first; /* first element */ \

}

#define LIST_HEAD_INITIALIZER(head) \

{ NULL }

#define LIST_ENTRY(type) \

struct { \

struct type *le_next; /* next element */ \

struct type **le_prev; /* address of previous next element */ \

}

/*

 * List access methods

 */

#define LIST_FIRST(head) ((head)->lh_first)

#define LIST_END(head) NULL

#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))

#define LIST_NEXT(elm, field) ((elm)->field.le_next)

#define LIST_FOREACH(var, head, field) \

for((var) = LIST_FIRST(head); \

    (var)!= LIST_END(head); \

    (var) = LIST_NEXT(var, field))

/*

 * List functions.

 */

#define LIST_INIT(head) do { \

LIST_FIRST(head) = LIST_END(head); \

} while (0)

#define LIST_INSERT_AFTER(listelm, elm, field) do { \

if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \

(listelm)->field.le_next->field.le_prev = \

    &(elm)->field.le_next; \

(listelm)->field.le_next = (elm); \

(elm)->field.le_prev = &(listelm)->field.le_next; \

} while (0)

#define LIST_INSERT_BEFORE(listelm, elm, field) do { \

(elm)->field.le_prev = (listelm)->field.le_prev; \

(elm)->field.le_next = (listelm); \

*(listelm)->field.le_prev = (elm); \

(listelm)->field.le_prev = &(elm)->field.le_next; \

} while (0)

#define LIST_INSERT_HEAD(head, elm, field) do { \

if (((elm)->field.le_next = (head)->lh_first) != NULL) \

(head)->lh_first->field.le_prev = &(elm)->field.le_next;\

(head)->lh_first = (elm); \

(elm)->field.le_prev = &(head)->lh_first; \

} while (0)

#define LIST_REMOVE(elm, field) do { \

if ((elm)->field.le_next != NULL) \

(elm)->field.le_next->field.le_prev = \

    (elm)->field.le_prev; \

*(elm)->field.le_prev = (elm)->field.le_next; \

} while (0)

#define LIST_REPLACE(elm, elm2, field) do { \

if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \

(elm2)->field.le_next->field.le_prev = \

    &(elm2)->field.le_next; \

(elm2)->field.le_prev = (elm)->field.le_prev; \

*(elm2)->field.le_prev = (elm2); \

} while (0)

/*

 * Simple queue definitions.

 */

#define SIMPLEQ_HEAD(name, type) \

struct name { \

struct type *sqh_first; /* first element */ \

struct type **sqh_last; /* addr of last next element */ \

}

#define SIMPLEQ_HEAD_INITIALIZER(head) \

{ NULL, &(head).sqh_first }

#define SIMPLEQ_ENTRY(type) \

struct { \

struct type *sqe_next; /* next element */ \

}

/*

 * Simple queue access methods.

 */

#define SIMPLEQ_FIRST(head)     ((head)->sqh_first)

#define SIMPLEQ_END(head)     NULL

#define SIMPLEQ_EMPTY(head)     (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))

#define SIMPLEQ_NEXT(elm, field)    ((elm)->field.sqe_next)

#define SIMPLEQ_FOREACH(var, head, field) \

for((var) = SIMPLEQ_FIRST(head); \

    (var) != SIMPLEQ_END(head); \

    (var) = SIMPLEQ_NEXT(var, field))

/*

 * Simple queue functions.

 */

#define SIMPLEQ_INIT(head) do { \

(head)->sqh_first = NULL; \

(head)->sqh_last = &(head)->sqh_first; \

} while (0)

#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \

if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \

(head)->sqh_last = &(elm)->field.sqe_next; \

(head)->sqh_first = (elm); \

} while (0)

#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \

(elm)->field.sqe_next = NULL; \

*(head)->sqh_last = (elm); \

(head)->sqh_last = &(elm)->field.sqe_next; \

} while (0)

#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \

if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\

(head)->sqh_last = &(elm)->field.sqe_next; \

(listelm)->field.sqe_next = (elm); \

} while (0)

#define SIMPLEQ_REMOVE_HEAD(head, elm, field) do { \

if (((head)->sqh_first = (elm)->field.sqe_next) == NULL) \

(head)->sqh_last = &(head)->sqh_first; \

} while (0)

/*

 * Tail queue definitions.

 */

#define TAILQ_HEAD(name, type) \

struct name { \

struct type *tqh_first; /* first element */ \

struct type **tqh_last; /* addr of last next element */ \

}

#define TAILQ_HEAD_INITIALIZER(head) \

{ NULL, &(head).tqh_first }

#define TAILQ_ENTRY(type) \

struct { \

struct type *tqe_next; /* next element */ \

struct type **tqe_prev; /* address of previous next element */ \

}

/*

 * tail queue access methods

 */

#define TAILQ_FIRST(head) ((head)->tqh_first)

#define TAILQ_END(head) NULL

#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)

#define TAILQ_LAST(head, headname) \

(*(((struct headname *)((head)->tqh_last ))->tqh_last))

/* XXX */

#define TAILQ_PREV(elm, headname, field) \

(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))

#define TAILQ_EMPTY(head) \

(TAILQ_FIRST(head) == TAILQ_END(head))

#define TAILQ_FOREACH(var, head, field) \

for((var) = TAILQ_FIRST(head); \

    (var) != TAILQ_END(head); \

    (var) = TAILQ_NEXT(var, field))

#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \

for((var) = TAILQ_LAST(head, headname); \

    (var) != TAILQ_END(head); \

    (var) = TAILQ_PREV(var, headname, field))

/*

 * Tail queue functions.

 */

#define TAILQ_INIT(head) do { \

(head)->tqh_first = NULL; \

(head)->tqh_last = &(head)->tqh_first; \

} while (0)

#define TAILQ_INSERT_HEAD(head, elm, field) do { \

if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \

(head)->tqh_first->field.tqe_prev = \

    &(elm)->field.tqe_next; \

else \

(head)->tqh_last = &(elm)->field.tqe_next; \

(head)->tqh_first = (elm); \

(elm)->field.tqe_prev = &(head)->tqh_first; \

} while (0)

#define TAILQ_INSERT_TAIL(head, elm, field) do { \

(elm)->field.tqe_next = NULL; \

(elm)->field.tqe_prev = (head)->tqh_last; \

*(head)->tqh_last = (elm); \

(head)->tqh_last = &(elm)->field.tqe_next; \

} while (0)

#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \

if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\

(elm)->field.tqe_next->field.tqe_prev = \

    &(elm)->field.tqe_next; \

else \

(head)->tqh_last = &(elm)->field.tqe_next; \

(listelm)->field.tqe_next = (elm); \

(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \

} while (0)

#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \

(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \

(elm)->field.tqe_next = (listelm); \

*(listelm)->field.tqe_prev = (elm); \

(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \

} while (0)

#define TAILQ_REMOVE(head, elm, field) do { \

if (((elm)->field.tqe_next) != NULL) \

(elm)->field.tqe_next->field.tqe_prev = \

    (elm)->field.tqe_prev; \

else \

(head)->tqh_last = (elm)->field.tqe_prev; \

*(elm)->field.tqe_prev = (elm)->field.tqe_next; \

} while (0)

#define TAILQ_REPLACE(head, elm, elm2, field) do { \

if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \

(elm2)->field.tqe_next->field.tqe_prev = \

    &(elm2)->field.tqe_next; \

else \

(head)->tqh_last = &(elm2)->field.tqe_next; \

(elm2)->field.tqe_prev = (elm)->field.tqe_prev; \

*(elm2)->field.tqe_prev = (elm2); \

} while (0)

/*

 * Circular queue definitions.

 */

#define CIRCLEQ_HEAD(name, type) \

struct name { \

struct type *cqh_first; /* first element */ \

struct type *cqh_last; /* last element */ \

}

#define CIRCLEQ_HEAD_INITIALIZER(head) \

{ CIRCLEQ_END(&head), CIRCLEQ_END(&head) }

#define CIRCLEQ_ENTRY(type) \

struct { \

struct type *cqe_next; /* next element */ \

struct type *cqe_prev; /* previous element */ \

}

/*

 * Circular queue access methods

 */

#define CIRCLEQ_FIRST(head) ((head)->cqh_first)

#define CIRCLEQ_LAST(head) ((head)->cqh_last)

#define CIRCLEQ_END(head) ((void *)(head))

#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)

#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)

#define CIRCLEQ_EMPTY(head) \

(CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))

#define CIRCLEQ_FOREACH(var, head, field) \

for((var) = CIRCLEQ_FIRST(head); \

    (var) != CIRCLEQ_END(head); \

    (var) = CIRCLEQ_NEXT(var, field))

#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \

for((var) = CIRCLEQ_LAST(head); \

    (var) != CIRCLEQ_END(head); \

    (var) = CIRCLEQ_PREV(var, field))

/*

 * Circular queue functions.

 */

#define CIRCLEQ_INIT(head) do { \

(head)->cqh_first = CIRCLEQ_END(head); \

(head)->cqh_last = CIRCLEQ_END(head); \

} while (0)

#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \

(elm)->field.cqe_next = (listelm)->field.cqe_next; \

(elm)->field.cqe_prev = (listelm); \

if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \

(head)->cqh_last = (elm); \

else \

(listelm)->field.cqe_next->field.cqe_prev = (elm); \

(listelm)->field.cqe_next = (elm); \

} while (0)

#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \

(elm)->field.cqe_next = (listelm); \

(elm)->field.cqe_prev = (listelm)->field.cqe_prev; \

if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \

(head)->cqh_first = (elm); \

else \

(listelm)->field.cqe_prev->field.cqe_next = (elm); \

(listelm)->field.cqe_prev = (elm); \

} while (0)

#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \

(elm)->field.cqe_next = (head)->cqh_first; \

(elm)->field.cqe_prev = CIRCLEQ_END(head); \

if ((head)->cqh_last == CIRCLEQ_END(head)) \

(head)->cqh_last = (elm); \

else \

(head)->cqh_first->field.cqe_prev = (elm); \

(head)->cqh_first = (elm); \

} while (0)

#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \

(elm)->field.cqe_next = CIRCLEQ_END(head); \

(elm)->field.cqe_prev = (head)->cqh_last; \

if ((head)->cqh_first == CIRCLEQ_END(head)) \

(head)->cqh_first = (elm); \

else \

(head)->cqh_last->field.cqe_next = (elm); \

(head)->cqh_last = (elm); \

} while (0)

#define CIRCLEQ_REMOVE(head, elm, field) do { \

if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \

(head)->cqh_last = (elm)->field.cqe_prev; \

else \

(elm)->field.cqe_next->field.cqe_prev = \

    (elm)->field.cqe_prev; \

if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \

(head)->cqh_first = (elm)->field.cqe_next; \

else \

(elm)->field.cqe_prev->field.cqe_next = \

    (elm)->field.cqe_next; \

} while (0)

#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \

if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \

    CIRCLEQ_END(head)) \

(head).cqh_last = (elm2); \

else \

(elm2)->field.cqe_next->field.cqe_prev = (elm2); \

if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \

    CIRCLEQ_END(head)) \

(head).cqh_first = (elm2); \

else \

(elm2)->field.cqe_prev->field.cqe_next = (elm2); \

} while (0)

#endif /* !_SYS_QUEUE_H_ */

上面就是全部的代码,对于tail queue 部分可能有两个宏不太好理解,就是TAILQ_LASTTAILQ_PREV, 下面说明下:

#define TAILQ_LAST(head, headname) \

(*(((struct headname *)((head)->tqh_last ))->tqh_last))

/* XXX */

#define TAILQ_PREV(elm, headname, field) \

(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))

这两个宏处理的方法是一样的,只要理解其中一个就都明白了。就以TAILQ_LAST为例吧,整个链表的结构图如下:

头部定义

#define TAILQ_HEAD(name, type) \

struct name { \

struct type *tqh_first; /* first element */ \

struct type **tqh_last; /* addr of last next element */ \

}

结点定义

#define TAILQ_ENTRY(type) \

struct { \

struct type *tqe_next; /* next element */ \

struct type **tqe_prev; /* address of previous next element */ \

}

1. (head)->tqh_last 取到的是最后一个元素中的tqe_next的地址,

2. 之所以要做struct headname *转换完全是为了获取到tqe_prev的值,也就是上一个元素的tqe_next的地址,但是又为什么非得转成这个而不是直接使用tail entry呢?

很简单,大家有没有发现结点定义其并没有类型名,因此要转换成tail entry就不可能了;还有就是headentry的存储结构是一样的,获取tqh_last的值也等于是在获取tqe_prev的值

3. 既然已经取到最后一个元素的前一个元素的tqe_next的地址,那么只有使用“*”取出它的值不就是最后一个元素的地址了吗!

确实处理的很巧妙,不得不为之惊叹。

最后,理解牛人们的东西并使用它们才能提高自己。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值