TAILQ 原始碼分析 用法示例

2021-08-03 22:55:44 字數 3651 閱讀 3888

這其實是 1994 年的老**,  在  sys/queue.h  中

queue.h    8.5 (berkeley) 8/20/94

一共提供了5個資料結構的封裝

1. 單鏈表 list    slist  省記憶體,少刪除,少插入

2. 雙向列表 list   list,  可惜只能頭部插入,

3.  單佇列 ****** queue   可頭尾插入。  頭尾移除還是快的。中間就慢了

4. tailq  就是本文分析的這個了, 雙鏈表佇列, 中間插入也快的。 就是耗點記憶體

5. 迴圈鍊錶

#include #include #include #include typedef struct animal animal;

typedef list_head(animallist, animal) animallist;

animal* newanimal(const char* name, int age)

void freeanimals(animal* a)

void showanimals(animallist* list)

}int main()

printf("list len = %d\n", listn);

printf("刪除 乙個動物 %s\n", a->name);

list_remove(a, p);

freeanimals(a);

showanimals(&animals);

// 清空

animal* next=null;

for (iter = animals.lh_first; iter; )

animals.lh_first = null;

showanimals(&animals);

printf("\n");

a = newanimal("frog", 1);

list_insert_head(&animals, a, p);

showanimals(&animals);

return 0;

}

清空比較麻煩,可以用下面這個巨集, 就是多搞了個臨時變數,先把next 指標存起來,這樣你迴圈內部就可以把當前指標

free 掉了。

#define	slist_foreach_safe(var, head, field, tvar)			\

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

(var) && ((tvar) = slist_next(var, field), 1); \

(var) = (tvar))

github 上 open62541原始碼,正是加了這個巨集。   

注意這裡有個逗號運算子

逗號運算子中的所有表示式都會被依次從左到又的執行,但是只保留最右邊的表示式結果,作為整個逗號語句的結果。

表示式1 , 表示式2 , 表示式3  

比如上面的,表示式3的結果將是整個語句的結果。

而 ((tvar) = slist_next(var, field), 1) 的結果就是  1 ,不論 tvar 到底是個啥

c語言的結構體變數的記憶體分布是連續的,首變數的位址就是整個結構體的位址

(c++的結構體要注意,變數的public protected private 屬性、繼承、可能導致記憶體分布不連續, 虛函式指標的位置...)

struct a

int n1;

int n2;

int n3;

}a;也就是說  

int *  pn1 = &a.n1;

a* pa = &a;

pa == pn1

我一旦拿到 n1的位址, 整個a的變數我都可以訪問了。

((a*)pn1)->n1;

((a*)pn1)->n2;

((a*)pn1)->n3;

struct qitem 

e;};

// 注意:head 和 entry 結構體的布局是一樣的。 next==first prev==tail

struct head head;

head.first = null;

head.tail = &head.first;

qitem item1=, item2=;

// 把它們依次加到末尾

tailq_insert_tail(&head, &item1, e);

tailq_insert_tail(&head, &item2, e);

// 我們把 tailq_insert_tail 展開 它相當於

pitem->next = null; // 尾部null

pitem->prev = phead->tail;

*phead->tail = pitem;

phead->tail = &pitem->next;

prev指向前乙個元素的next的位址

item2.e.prev == &item1.e.next

於是 *item2.e.prev == item1.e.next // 前乙個元素的next , 不就是自己麼

== &item2

也就是說 任意節點的 *prev 它的結果就是其本身 ---- 知識點1

qitem* pitem2 = &item2;

qitem* me = *pitem2->e.prev;

me == pitem2;

下面來解釋

tailq_last(&head, head);

head的first指向第乙個元素

head的tail指向最後乙個元素 next 的位址

而我現在要的是 最後乙個元素

在本例中

head->tail == &item2.e.next

光知道你的next位址有啥用呢,我要的是item2的位址啊~~~~~

next 是結構體的首元素

結構體首元素的位址等於結構體的位址

於是 head->tail == &item2.e.next == &item2.e

((struct entry*)head->tail) == &item2.e

((struct entry*)head->tail)->prev == item2.e.prev 不就可以訪問到了prev了麼

根據知識點1, *item2.e.prev == &item2;

tailq_last(&head, head) 寫成下面這樣就行了

*((struct entry*)head->tail)->prev

tailq 實際**中 struct entry 是匿名的結構體,

採用 同位序的 struct head 取代, prev == tail next == first

於是 *((struct entry*)head->tail)->prev

*((struct head*)head->tail)->tail

在tailq 看來 struct entry 和 struct head 是一模一樣的東西

同理tailq_prev(elm, headname, field);

*((struct head*)(elm->field.prev))->tail

Duilib 原始碼分析(一)示例推演

duilib示範例子 使用duilib編寫乙個介面軟體,本質上還是乙個win32的軟體,只不過這個軟體的介面不使用windows自帶的控制項,而是交給duilib繪製介面。關於訊息處理,底層還是處理window訊息,但duilib會進一步轉化成duilib訊息,方便編寫響應的邏輯。最簡單的win32...

angular示例原始碼集合

這個是用於父元件向子元件投射內容 最簡單的demo 父親模板 這裡放內容 子模板 angular cli腳手架搭建,沒有執行ng eject angular cli腳手架搭建,並且執行了ng eject 關於指令的基本使用,介紹了屬性指令 結構指令等指令以及相應的使用方式 關於我寫的以及一些收集到的...

spring原始碼分析 spring原始碼分析

1.spring 執行原理 spring 啟動時讀取應用程式提供的 bean 配置資訊,並在 spring 容器中生成乙份相應的 bean 配置登錄檔,然後根據這張登錄檔例項化 bean,裝配好 bean 之間的依賴關係,為上 層應用提供準備就緒的執行環境。二 spring 原始碼分析 1.1spr...