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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

链表之CIRCLEQ

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

文章目錄

    • 頭文件
    • 例子
    • 代碼分析
      • CIRCLEQ_ENTRY 和 CIRCLEQ_HEAD
      • CIRCLEQ_HEAD_INITIALIZER
      • CIRCLEQ_FIRST 和 CIRCLEQ_LAST
      • CIRCLEQ_PREV 和 CIRCLEQ_NEXT
      • CIRCLEQ_INIT
      • CIRCLEQ_EMPTY
      • CIRCLEQ_INSERT_TAIL
      • CIRCLEQ_INSERT_HEAD
      • CIRCLEQ_INSERT_AFTER
      • CIRCLEQ_INSERT_BEFORE
      • CIRCLEQ_FOREACH
      • CIRCLEQ_FOREACH_REVERSE
      • CIRCLEQ_REMOVE

頭文件

頭文件來自我最近看的項目: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】

    總結

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

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