日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人工智能 > ChatGpt >内容正文

ChatGpt

链表之STAILQ

發布時間:2025/3/15 ChatGpt 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 链表之STAILQ 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

    • STAILQ 示意圖
    • 接口和實現
    • 舉例
    • 代碼分析
      • STAILQ_ENTRY 和 STAILQ_HEAD
      • STAILQ_INIT 和 STAILQ_INSERT_HEAD
      • STAILQ_INSERT_TAIL
      • STAILQ_INSERT_AFTER
      • STAILQ_REMOVE
      • STAILQ_FIRST 和 STAILQ_REMOVE_HEAD
      • STAILQ_FOREACH
      • STAILQ_NEXT
      • STAILQ_EMPTY
      • STAILQ_LAST

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 queuehead */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 queuehead */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)->stqh_last = &(&head)->stqh_first;

    如果刪除之前,僅有一個節點,那么需要修改 stqh_last,也就是第 2 行

    如果有多個節點,那么只需要修改 stqh_first(第 1 行的前半句)

    STAILQ_FOREACH

    /* Forward traversal */ STAILQ_FOREACH(np, &head, entries)printf("%i\n", np->data);

    STAILQ_FOREACH(np, &head, entries) 用來遍歷鏈表的每個節點。第一個參數是臨時變量,指向當前的節點,第二個參數是表頭的地址,第三個 entries 是無標簽結構體的成員名。

    第 2 行展開:

    for ((np) = ((&head)->stqh_first); (np); (np) = ((np)->entries.stqe_next))printf("%i\n", np->data);

    典型的遍歷。注意,這種遍歷是不能刪除的,因為如果把 np 指向的節點刪除了,

    (np)->entries.stqe_next 這句就不對了。

    STAILQ_NEXT

    /* TailQ deletion */n1 = STAILQ_FIRST(&head);while (n1 != NULL) {n2 = STAILQ_NEXT(n1, entries);free(n1);n1 = n2;}

    STAILQ_NEXT(n1, entries) 表示取 n1 的下一個節點

    第 4 行展開是 n2 = ((n1)->entries.stqe_next); 比較簡單。

    這段代碼也展示了如何刪除整個鏈表。

    還有幾個宏,例子里面沒有用到,我們也看一下。

    STAILQ_EMPTY

    #define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)

    這個很好理解,不解釋了。

    STAILQ_LAST

    #define STAILQ_LAST(head, type, field) \(STAILQ_EMPTY(head) ? \NULL : \((struct type *) \((char *)((head)->stqh_last) - offsetof(struct type, field))))

    注意,我的 Linux 操作系統上,并不包含這個宏,所以,如果要用,需要手工把這段代碼包含進去。

    這個宏的意思找到隊列的最后一個節點。它是這么用的:

    n1 = STAILQ_LAST(&head, entry, entries);

    本文開始的時候,有

    struct entry {int data;STAILQ_ENTRY(entry) entries; /* Singly linked tail queue */ };STAILQ_HEAD(stailhead, entry);

    n1 = STAILQ_LAST(&head, entry, entries) 的后兩個參數要對應第 3 行的定義。

    我們回頭看 STAILQ_LAST 的代碼

    #define STAILQ_LAST(head, type, field) \(STAILQ_EMPTY(head) ? \NULL : \((struct type *) \((char *)((head)->stqh_last) - offsetof(struct type, field))))

    如果鏈表為空,就返回 NULL,如果不為空呢?當然是順著 (head)->stqh_last 找對最后一個節點。

    問題是,(head)->stqh_last 指向的是最后一個節點的 entries.stqe_next 域

    struct entry {int data;struct { struct entry *stqe_next; } entries; };

    也就是第 4 行,(head)->stqh_last 的值是 struct entry *stqe_next 的地址,而不是 struct entry 的地址,所以需要轉換一下。轉換需要用到

    offsetof(struct type, field)

    這也是一個宏,代碼大概在 /usr/src/linux-headers-4.8.0-36-generic/include/linux/stddef.h

    #define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER)

    這里的 TYPE 表示某個結構體類型,MEMBER 表示結構體中的一個成員,這個宏求出了成員在結構體中的位置偏移(以字節為單位)

    假設一個結構體變量(類型是 struct entry)的起始地址是 0x1234,它里面有個成員是 data,請問 data 變量的地址是多少?

    可以這樣求:

    &((struct entry *)0x1234)->data

    ->比類型轉換的優先級高,所以要加括號。((struct entry *)0x1234)->data 就得到了成員,再取地址,得到成員的地址,再來一個類型轉換,轉成無符號整形:

    (size_t)&((struct entry *)0x1234)->data

    終于得到成員的起始地址了,這時候我們看看它對于結構體變量的起始地址的偏移是多少?也就是用成員的地址減去結構體變量的地址:

    (size_t)&((struct entry *)0x1234)->data - 0x1234

    不難看出,這里的 0x1234 換成什么數字都可以,因為偏移是一個相對量,和起始地址無關

    為了簡單,那就把 0x1234 換成 0 吧。

    我們做一般化的處理,把 struct entry 叫 TYPE,把 data 叫 MEMBER,所以就有了

    ((size_t)&((TYPE *)0)->MEMBER)

    咱們回到正題上來,(head)->stqh_last 的值是 struct entry *stqe_next 的地址,而不是 struct entry 的地址,*如果我們知道 struct entry stqe_next 相對于 struct entry 的偏移 x,那么用 (head)->stqh_last 減去 x 就可以了。

    利用 offsetof(struct type, field) 來求偏移,同時注意到 struct entry *stqe_next 和 entries 是同一個地址,所以偏移是:offsetof(struct entry , entries )

    最后一個節點的地址是:

    (struct entry *)((char*)((head)->stqh_last) - offsetof(struct entry, entries)))

    把上面這個式子一般化,entry 換成 type,entries 換成 field,就得到了代碼中的:

    ((struct type *) \((char *)((head)->stqh_last) - offsetof(struct type, field))))

    其實還有更簡單的方法,直接利用內核里面的 container_of 宏,這里只做個提示,就不展開了。

    【end】

    總結

    以上是生活随笔為你收集整理的链表之STAILQ的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。