链表之STAILQ
Posted 车子 chezi
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了链表之STAILQ相关的知识,希望对你有一定的参考价值。
文章目录
STAILQ 示意图
STAILQ 和 LIST 的不同是,head 里面有个指针 stqh_last,指向最后一个节点的 stqe_next
注意:有的地方也把 STAILQ 叫 simple queue,将接口前缀改为 SIMPLEQ_
接口和实现
先贴代码。(注意:不同版本的代码可能不同)
/*
* Singly-linked Tail queue declarations.
*/
#define STAILQ_HEAD(name, type) \\
struct name \\
struct type *stqh_first;/* first element */ \\
struct type **stqh_last;/* addr of last next element */ \\
#define STAILQ_HEAD_INITIALIZER(head) \\
NULL, &(head).stqh_first
#define STAILQ_ENTRY(type) \\
struct \\
struct type *stqe_next; /* next element */ \\
/*
* Singly-linked Tail queue functions.
*/
#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
#define STAILQ_FIRST(head) ((head)->stqh_first)
#define STAILQ_FOREACH(var, head, field) \\
for((var) = STAILQ_FIRST((head)); \\
(var); \\
(var) = STAILQ_NEXT((var), field))
#define STAILQ_INIT(head) do \\
STAILQ_FIRST((head)) = NULL; \\
(head)->stqh_last = &STAILQ_FIRST((head)); \\
while (0)
#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do \\
if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\\
(head)->stqh_last = &STAILQ_NEXT((elm), field); \\
STAILQ_NEXT((tqelm), field) = (elm); \\
while (0)
#define STAILQ_INSERT_HEAD(head, elm, field) do \\
if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \\
(head)->stqh_last = &STAILQ_NEXT((elm), field); \\
STAILQ_FIRST((head)) = (elm); \\
while (0)
#define STAILQ_INSERT_TAIL(head, elm, field) do \\
STAILQ_NEXT((elm), field) = NULL; \\
*(head)->stqh_last = (elm); \\
(head)->stqh_last = &STAILQ_NEXT((elm), field); \\
while (0)
#define STAILQ_LAST(head, type, field) \\
(STAILQ_EMPTY(head) ? \\
NULL : \\
((struct type *) \\
((char *)((head)->stqh_last) - offsetof(struct type, field))))
#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
#define STAILQ_REMOVE(head, elm, type, field) do \\
if (STAILQ_FIRST((head)) == (elm)) \\
STAILQ_REMOVE_HEAD(head, field); \\
\\
else \\
struct type *curelm = STAILQ_FIRST((head)); \\
while (STAILQ_NEXT(curelm, field) != (elm)) \\
curelm = STAILQ_NEXT(curelm, field); \\
if ((STAILQ_NEXT(curelm, field) = \\
STAILQ_NEXT(STAILQ_NEXT(curelm, field), field)) == NULL)\\
(head)->stqh_last = &STAILQ_NEXT((curelm), field);\\
\\
while (0)
#define STAILQ_REMOVE_HEAD(head, field) do \\
if ((STAILQ_FIRST((head)) = \\
STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \\
(head)->stqh_last = &STAILQ_FIRST((head)); \\
while (0)
#define STAILQ_REMOVE_HEAD_UNTIL(head, elm, field) do \\
if ((STAILQ_FIRST((head)) = STAILQ_NEXT((elm), field)) == NULL) \\
(head)->stqh_last = &STAILQ_FIRST((head)); \\
while (0)
#define STAILQ_REMOVE_AFTER(head, elm, field) do \\
if ((STAILQ_NEXT(elm, field) = \\
STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \\
(head)->stqh_last = &STAILQ_NEXT((elm), field); \\
while (0)
举例
咱们看个例子,例子来自:https://manpages.courier-mta.org/htmlman3/stailq.3.html
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/queue.h>
struct entry
int data;
STAILQ_ENTRY(entry) entries; /* Singly linked tail queue */
;
STAILQ_HEAD(stailhead, entry);
int
main(void)
struct entry *n1, *n2, *n3, *np;
struct stailhead head; /* Singly linked tail queue
head */
STAILQ_INIT(&head); /* Initialize the queue */
n1 = malloc(sizeof(struct entry)); /* Insert at the head */
STAILQ_INSERT_HEAD(&head, n1, entries);
n1 = malloc(sizeof(struct entry)); /* Insert at the tail */
STAILQ_INSERT_TAIL(&head, n1, entries);
n2 = malloc(sizeof(struct entry)); /* Insert after */
STAILQ_INSERT_AFTER(&head, n1, n2, entries);
STAILQ_REMOVE(&head, n2, entry, entries); /* Deletion */
free(n2);
n3 = STAILQ_FIRST(&head);
STAILQ_REMOVE_HEAD(&head, entries); /* Deletion from the head */
free(n3);
n1 = STAILQ_FIRST(&head);
n1->data = 0;
for (int i = 1; i < 5; i++)
n1 = malloc(sizeof(struct entry));
STAILQ_INSERT_HEAD(&head, n1, entries);
n1->data = i;
/* Forward traversal */
STAILQ_FOREACH(np, &head, entries)
printf("%i\\n", np->data);
/* TailQ deletion */
n1 = STAILQ_FIRST(&head);
while (n1 != NULL)
n2 = STAILQ_NEXT(n1, entries);
free(n1);
n1 = n2;
STAILQ_INIT(&head);
exit(EXIT_SUCCESS);
代码分析
STAILQ_ENTRY 和 STAILQ_HEAD
struct entry
int data;
STAILQ_ENTRY(entry) entries; /* Singly linked tail queue */
;
STAILQ_HEAD(stailhead, entry);
宏替换后是
struct entry
int data;
struct
struct entry *stqe_next;
entries;
;
struct stailhead
struct entry *stqh_first;
struct entry **stqh_last;
;
其实和 SLIST 差不多,唯一的不同是第 10 行,多了一个指向尾节点的指针(准确地说是指向尾节点的 stqe_next)
STAILQ_INIT 和 STAILQ_INSERT_HEAD
struct entry *n1, *n2, *n3, *np;
struct stailhead head; /* Singly linked tail queue
head */
STAILQ_INIT(&head); /* Initialize the queue */
n1 = malloc(sizeof(struct entry)); /* Insert at the head */
STAILQ_INSERT_HEAD(&head, n1, entries);
5:宏展开是
(&head)->stqh_first = ((void *)0);
(&head)->stqh_last = &(&head)->stqh_first;
链表的初始化为空,stqh_first 等于 NULL,stqh_last 指向自己的 stqh_first
8:宏展开是
if (((n1)->entries.stqe_next = (&head)->stqh_first) == ((void *)0))
(&head)->stqh_last = &(n1)->entries.stqe_next;
(&head)->stqh_first = (n1);
把 n1 插入链表的第一个位置,会影响哪个指针的值呢?
- stqh_first
- n1 的 stqe_next
- 如果链表为空,那么插入 n1 后,stqh_last 也要改变
第 1 行解决了 2
第 2 行解决了 3
第 3 行解决了 1
STAILQ_INSERT_TAIL
n1 = malloc(sizeof(struct entry)); /* Insert at the tail */
STAILQ_INSERT_TAIL(&head, n1, entries);
2:宏替换是
(n1)->entries.stqe_next = ((void *)0);
*(&head)->stqh_last = (n1);
(&head)->stqh_last = &(n1)->entries.stqe_next;
把 n1 插入链表的最后一个位置,会影响哪些指针的值呢?
- stqh_last
- n1 的 stqe_next,需要为 NULL
- 原先的最后一个节点(假设是 n8)的 stqe_next
第 1 行解决了 2
第 2 行有点麻烦,(&head)->stqh_last 指向 n8 节点的 stqe_next,*(&head)->stqh_last 就是 n8 节点的 stqe_next,赋值 n1 给它,解决了 3
第 3 行解决了 1
STAILQ_INSERT_AFTER
n2 = malloc(sizeof(struct entry)); /* Insert after */
STAILQ_INSERT_AFTER(&head, n1, n2, entries);
第 2 行的意思是把 n2 插入到 n1 的后面
代码展开后是
if (((n2)->entries.stqe_next = (n1)->entries.stqe_next) == ((void *)0))
(&head)->stqh_last = &(n2)->entries.stqe_next;
(n1)->entries.stqe_next = (n2);
把 n2 插入到 n1 的后面,会影响哪些指针的值呢?
- n1 的 stqe_next
- n2 的 stqe_next
- 如果 n2 是最后一个节点,还要修改 (&head)->stqh_last
第 1 行的 (n2)->entries.stqe_next = (n1)->entries.stqe_next)
解决了 2
第 3 行解决了 1
第 2 行解决了 3
STAILQ_REMOVE
STAILQ_REMOVE(&head, n2, entry, entries); /* Deletion */
free(n2);
第 1 行展开是,有点长
if ((&head)->stqh_first == (n2))
if ((((&head))->stqh_first = ((&head))->stqh_first->entries.stqe_next) == ((void *)0))
((&head))->stqh_last = &((&head))->stqh_first;
else
struct entry *curelm = (&head)->stqh_first;
while (curelm->entries.stqe_next != (n2))
curelm = curelm->entries.stqe_next;
if ((curelm->entries.stqe_next = curelm->entries.stqe_next->entries.stqe_next) == ((void *)0))
(&head)->stqh_last = &(curelm)->entries.stqe_next;
分 2 种情况。n2 是不是第一个节点(第 1 行代码),如果是,需要修改的指针有
- (&head)->stqh_first
- 如果 n2 是仅有的一个节点,那么还需要修改 (&head))->stqh_last
第 2 行的前半句解决了 1
第 3 行解决了 2
如果 n2 不是第一个节点,那就执行 5-10,先查找 n2 (6-7 行),当找到的时候, curelm 代表 n2 前面的那个节点。此时需要修改的指针有:
- curelm 的 stqe_next
- 如果 n2 是最后一个节点,删除它后需要修改 (&head))->stqh_last
第 8 行的前半句解决了 1
第 9 行解决了 2
STAILQ_FIRST 和 STAILQ_REMOVE_HEAD
n3 = STAILQ_FIRST(&head);
STAILQ_REMOVE_HEAD(&head, entries); /* Deletion from the head */
free(n3);
STAILQ_FIRST 比较简单,就是取链表的第一个节点,注意,不是删除
第 1 行展开:n3 = ((&head)->stqh_first);
STAILQ_REMOVE_HEAD 是删除第一个节点
第 2 行展开:
if (((&head)->stqh_first = (&head)->stqh_first->entries.stqe_next) == ((void *)0))
(&head)链表之STAILQ