文章目录
头文件
头文件来自我最近看的项目:apache-mynewt-core-1.9.0\sys\sys\include\sys\queue.h
仅截取部分。
/*
* Circular queue declarations.
*/
#define CIRCLEQ_HEAD(name, type) \
struct name { \
struct type *cqh_first; /* first element */ \
struct type *cqh_last; /* last element */ \
}
#define CIRCLEQ_HEAD_INITIALIZER(head) \
{ (void *)&(head), (void *)&(head) }
#define CIRCLEQ_ENTRY(type) \
struct { \
struct type *cqe_next; /* next element */ \
struct type *cqe_prev; /* previous element */ \
}
/*
* Circular queue functions.
*/
#define CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head))
#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
#define CIRCLEQ_FOREACH(var, head, field) \
for ((var) = CIRCLEQ_FIRST((head)); \
(var) != (void *)(head) || ((var) = NULL); \
(var) = CIRCLEQ_NEXT((var), field))
#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
for ((var) = CIRCLEQ_LAST((head)); \
(var) != (void *)(head) || ((var) = NULL); \
(var) = CIRCLEQ_PREV((var), field))
#define CIRCLEQ_INIT(head) do { \
CIRCLEQ_FIRST((head)) = (void *)(head); \
CIRCLEQ_LAST((head)) = (void *)(head); \
} while (0)
#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
CIRCLEQ_NEXT((elm), field) = CIRCLEQ_NEXT((listelm), field); \
CIRCLEQ_PREV((elm), field) = (listelm); \
if (CIRCLEQ_NEXT((listelm), field) == (void *)(head)) \
CIRCLEQ_LAST((head)) = (elm); \
else \
CIRCLEQ_PREV(CIRCLEQ_NEXT((listelm), field), field) = (elm);\
CIRCLEQ_NEXT((listelm), field) = (elm); \
} while (0)
#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
CIRCLEQ_NEXT((elm), field) = (listelm); \
CIRCLEQ_PREV((elm), field) = CIRCLEQ_PREV((listelm), field); \
if (CIRCLEQ_PREV((listelm), field) == (void *)(head)) \
CIRCLEQ_FIRST((head)) = (elm); \
else \
CIRCLEQ_NEXT(CIRCLEQ_PREV((listelm), field), field) = (elm);\
CIRCLEQ_PREV((listelm), field) = (elm); \
} while (0)
#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
CIRCLEQ_NEXT((elm), field) = CIRCLEQ_FIRST((head)); \
CIRCLEQ_PREV((elm), field) = (void *)(head); \
if (CIRCLEQ_LAST((head)) == (void *)(head)) \
CIRCLEQ_LAST((head)) = (elm); \
else \
CIRCLEQ_PREV(CIRCLEQ_FIRST((head)), field) = (elm); \
CIRCLEQ_FIRST((head)) = (elm); \
} while (0)
#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
CIRCLEQ_NEXT((elm), field) = (void *)(head); \
CIRCLEQ_PREV((elm), field) = CIRCLEQ_LAST((head)); \
if (CIRCLEQ_FIRST((head)) == (void *)(head)) \
CIRCLEQ_FIRST((head)) = (elm); \
else \
CIRCLEQ_NEXT(CIRCLEQ_LAST((head)), field) = (elm); \
CIRCLEQ_LAST((head)) = (elm); \
} while (0)
#define CIRCLEQ_LAST(head) ((head)->cqh_last)
#define CIRCLEQ_NEXT(elm,field) ((elm)->field.cqe_next)
#define CIRCLEQ_PREV(elm,field) ((elm)->field.cqe_prev)
#define CIRCLEQ_REMOVE(head, elm, field) do { \
if (CIRCLEQ_NEXT((elm), field) == (void *)(head)) \
CIRCLEQ_LAST((head)) = CIRCLEQ_PREV((elm), field); \
else \
CIRCLEQ_PREV(CIRCLEQ_NEXT((elm), field), field) = \
CIRCLEQ_PREV((elm), field); \
if (CIRCLEQ_PREV((elm), field) == (void *)(head)) \
CIRCLEQ_FIRST((head)) = CIRCLEQ_NEXT((elm), field); \
else \
CIRCLEQ_NEXT(CIRCLEQ_PREV((elm), field), field) = \
CIRCLEQ_NEXT((elm), field); \
} while (0)
例子
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include "bsd_queue.h" // 就是我上面贴的代码
struct entry {
int data;
CIRCLEQ_ENTRY(entry) entries; /* Queue */
};
CIRCLEQ_HEAD(circlehead, entry);
int
main(void)
{
struct entry *n1, *n2, *n3, *np;
struct circlehead head; /* Queue head */
int i;
CIRCLEQ_INIT(&head); /* Initialize the queue */
n1 = malloc(sizeof(struct entry)); /* Insert at the head */
CIRCLEQ_INSERT_HEAD(&head, n1, entries);
n1 = malloc(sizeof(struct entry)); /* Insert at the tail */
CIRCLEQ_INSERT_TAIL(&head, n1, entries);
n2 = malloc(sizeof(struct entry)); /* Insert after */
CIRCLEQ_INSERT_AFTER(&head, n1, n2, entries);
n3 = malloc(sizeof(struct entry)); /* Insert before */
CIRCLEQ_INSERT_BEFORE(&head, n2, n3, entries);
CIRCLEQ_REMOVE(&head, n2, entries); /* Deletion */
free(n2);
/* Forward traversal */
i = 0;
CIRCLEQ_FOREACH(np, &head, entries)
np->data = i++;
/* Reverse traversal */
CIRCLEQ_FOREACH_REVERSE(np, &head, entries)
printf("%i\n", np->data);
/* Queue deletion */
n1 = CIRCLEQ_FIRST(&head);
while (n1 != (void *)&head) {
n2 = CIRCLEQ_NEXT(n1, entries);
free(n1);
n1 = n2;
}
CIRCLEQ_INIT(&head);
exit(EXIT_SUCCESS);
}
代码分析
我们一点一点看上面的例子。
先来一张我自己绘制的示意图。红色表示我觉得不太“正常”的地方,指针的类型不匹配,需要强制转换。
CIRCLEQ_ENTRY 和 CIRCLEQ_HEAD
struct entry {
int data;
CIRCLEQ_ENTRY(entry) entries; /* Queue */
};
CIRCLEQ_HEAD(circlehead, entry);
宏展开是:
struct entry {
int data;
struct {
struct entry *cqe_next;
struct entry *cqe_prev;
} entries;
};
struct circlehead {
struct entry *cqh_first;
struct entry *cqh_last;
};
和其他链表不同,这里没有用二级指针,cqe_prev 和 cqh_last 都是 struct entry * 类型。(为什么呢)
我前面写的文章都是研究宏展开后的代码,这次我们换一个思路,直接看宏的定义
CIRCLEQ_HEAD_INITIALIZER
#define CIRCLEQ_HEAD_INITIALIZER(head) \
{ (void *)&(head), (void *)&(head) }
初始化一个链表后,表头的两个指针都指向自己。
CIRCLEQ_FIRST 和 CIRCLEQ_LAST
#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
#define CIRCLEQ_LAST(head) ((head)->cqh_last)
这两个很好理解,不解释了。
CIRCLEQ_PREV 和 CIRCLEQ_NEXT
#define CIRCLEQ_PREV(elm,field) ((elm)->field.cqe_prev)
#define CIRCLEQ_NEXT(elm,field) ((elm)->field.cqe_next)
field 就是最开始定义的 entries
CIRCLEQ_INIT
#define CIRCLEQ_INIT(head) do { \
CIRCLEQ_FIRST((head)) = (void *)(head); \
CIRCLEQ_LAST((head)) = (void *)(head); \
} while (0)
初始化一个链表后,表头的两个指针都指向自己。如下图
CIRCLEQ_EMPTY
#define CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head))
CIRCLEQ_INSERT_TAIL
#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
CIRCLEQ_NEXT((elm), field) = (void *)(head); \
CIRCLEQ_PREV((elm), field) = CIRCLEQ_LAST((head)); \
if (CIRCLEQ_FIRST((head)) == (void *)(head)) \
CIRCLEQ_FIRST((head)) = (elm); \
else \
CIRCLEQ_NEXT(CIRCLEQ_LAST((head)), field) = (elm); \
CIRCLEQ_LAST((head)) = (elm); \
} while (0)
把 elm 插入到链表的尾部,都需要改变哪些指针呢?
- elm 的 field.cqe_next,因为它是最后一个,所以它应该等于 (void *)(head)
- elm 的 field.cqe_prev
- 插入后,elm 前面的那个节点的 field.cqe_next(如果前面的节点是表头,那就修改
CIRCLEQ_FIRST(head)
) - 修改 (head)->cqh_last 指向 elm
代码第 2、3 行分别解决了 1、2
代码第 4、5 行解决了 3 的括号中的情况
第 7 行解决了 3,elm 前面的那个节点就是原来最后的节点,CIRCLEQ_LAST(head),它的 cqe_next 就是 CIRCLEQ_NEXT(CIRCLEQ_LAST((head)), field)
第 8 行解决了 4
CIRCLEQ_INSERT_HEAD
#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
CIRCLEQ_NEXT((elm), field) = CIRCLEQ_FIRST((head)); \
CIRCLEQ_PREV((elm), field) = (void *)(head); \
if (CIRCLEQ_LAST((head)) == (void *)(head)) \
CIRCLEQ_LAST((head)) = (elm); \
else \
CIRCLEQ_PREV(CIRCLEQ_FIRST((head)), field) = (elm); \
CIRCLEQ_FIRST((head)) = (elm); \
} while (0)
把 elm 插入到链表的头部,都需要改变哪些指针呢?
- elm 的 field.cqe_next
- elm 的 field.cqe_prev,它应该等于 (void *)(head)
- (head)->cqh_first
- 插入后,elm 后面的那个节点的 field.cqe_prev(如果插入 elm 前链表为空,那就修改
CIRCLEQ_LAST(head)
)
第 2 行解决了 1
第 3 行解决了 2
第 4-5 行解决了 4 的括号中的情况
第 7 行解决了 4
第 8 行解决了 3
代码 4-7 行说明,因为没有用二级指针,所以头插的时候需要分情况考虑。但是不用二级指针也有好处,就是代码更好理解。
CIRCLEQ_INSERT_AFTER
#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
CIRCLEQ_NEXT((elm), field) = CIRCLEQ_NEXT((listelm), field); \
CIRCLEQ_PREV((elm), field) = (listelm); \
if (CIRCLEQ_NEXT((listelm), field) == (void *)(head)) \
CIRCLEQ_LAST((head)) = (elm); \
else \
CIRCLEQ_PREV(CIRCLEQ_NEXT((listelm), field), field) = (elm);\
CIRCLEQ_NEXT((listelm), field) = (elm); \
} while (0)
要把 elm 插入到 listelm 元素的后面,要修改哪些指针呢?
- elm 的 field.cqe_next
- elm 的 field.cqe_prev
- listelm 的 field.cqe_next
- 插入之前,listelm 后面的那个节点的 field.cqe_prev(如果 listelm 后面没有节点了,就修改
CIRCLEQ_LAST(head)
)
2-3 行分别解决了 1、2
4-5 行解决了 4 括号里的情况
第 7 行解决了 4
第 8 行解决了 3
CIRCLEQ_INSERT_BEFORE
#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
CIRCLEQ_NEXT((elm), field) = (listelm); \
CIRCLEQ_PREV((elm), field) = CIRCLEQ_PREV((listelm), field); \
if (CIRCLEQ_PREV((listelm), field) == (void *)(head)) \
CIRCLEQ_FIRST((head)) = (elm); \
else \
CIRCLEQ_NEXT(CIRCLEQ_PREV((listelm), field), field) = (elm);\
CIRCLEQ_PREV((listelm), field) = (elm); \
} while (0)
要把 elm 插入到 listelm 元素的前面,要修改哪些指针呢?
- elm 的 field.cqe_next
- elm 的 field.cqe_prev
- listelm 的 field.cqe_prev
- 插入之前,listelm 前面的那个节点的 field.cqe_next(如果 listelm 前面是表头,就修改
CIRCLEQ_FIRST(head)
)
2-3 行分别解决了 1、2
4-5 行解决了 4 括号里的情况
第 7 行解决了 4
第 8 行解决了 3
CIRCLEQ_FOREACH
#define CIRCLEQ_FOREACH(var, head, field) \
for ((var) = CIRCLEQ_FIRST((head)); \
(var) != (void *)(head) || ((var) = NULL); \
(var) = CIRCLEQ_NEXT((var), field))
顺序遍历,只要 (var) != (void *)(head)
就会往下进行,如果等于 head,根据 || 运算符的求值规则,会执行 (var) = NULL
,这个表达式的结果为 false,于是终止 for 循环
CIRCLEQ_FOREACH_REVERSE
#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
for ((var) = CIRCLEQ_LAST((head)); \
(var) != (void *)(head) || ((var) = NULL); \
(var) = CIRCLEQ_PREV((var), field))
逆序遍历,和顺序遍历类似,不解释了。
CIRCLEQ_REMOVE
#define CIRCLEQ_REMOVE(head, elm, field) do { \
if (CIRCLEQ_NEXT((elm), field) == (void *)(head)) \
CIRCLEQ_LAST((head)) = CIRCLEQ_PREV((elm), field); \
else \
CIRCLEQ_PREV(CIRCLEQ_NEXT((elm), field), field) = \
CIRCLEQ_PREV((elm), field); \
if (CIRCLEQ_PREV((elm), field) == (void *)(head)) \
CIRCLEQ_FIRST((head)) = CIRCLEQ_NEXT((elm), field); \
else \
CIRCLEQ_NEXT(CIRCLEQ_PREV((elm), field), field) = \
CIRCLEQ_NEXT((elm), field); \
} while (0)
移除节点 elm,需要修改那些指针呢?
-
elm 前面那个节点的 field.cqe_next
-
elm 后面那个节点的 field.cqe_prev
-
对于 1,如果 elm 前面的节点是 head,那就需要修改 CIRCLEQ_FIRST(head)
-
对于 2, 如果 elm 后面没有节点了,那就需要修改 CIRCLEQ_LAST(head)
代码 2-3 行解决了 4
5-6 行解决了 2
代码 7-8 行解决了 3
10-11 解决了 1
【end】