# 2021q1 Homework2 (quiz2) contributed by < `mickey30606` > ## 測驗 1 ### list.h ```c= #ifndef container_of #define container_of(ptr, type, member) \ __extension__({ \ const __typeof__(((type *) 0)->member) *__pmember = (ptr); \ (type *) ((char *) __pmember - offsetof(type, member)); \ }) #endif ``` 因為使用 ```({})``` ,所以前面加一個 ```__extension__``` 可以不顯示 gcc 的警告訊息。 沒看過的用法 ==```__typeof__(((type *) 0)->member)```== 這個可以當 member 的 type 使用。 突然發現下面有清楚的解釋。 ![](https://i.imgur.com/SAOa0ie.png) 這樣就可以知道原本這個 struct 的位址。 --- ```c= struct list_head { struct list_head *prev, *next; }; ``` 建立一個 struct 的型別,用來作為 doubly-linked list 的節點。 --- ```c= #define LIST_HEAD(head) struct list_head head = {&(head), &(head)} ``` 宣告一個 list_head 然後初始化,將兩邊都指向自己。 ```graphviz digraph p1 { list_head [label = "<f0>|<f1>head|<f2>" shape = record ]; list_head:f0 -> list_head [arrowhead = vee, tailclip=true] list_head:f2 -> list_head } ``` --- ```c= static inline void INIT_LIST_HEAD(struct list_head *head) { head->next = head; head->prev = head; } ``` 初始化一個空的 list_head 。 --- ```c= static inline void list_add_tail(struct list_head *node, struct list_head *head) { struct list_head *prev = head->prev; prev->next = node; node->next = head; node->prev = prev; head->prev = node; } ``` 將 node 加入整個 doubly-linked list 的最尾端。 ```graphviz digraph p1{ #subgraph nn{ #rankdir = TB # newnode[label = "{<newp>|new node|<newn>}" shape = record] # newnode:newn ->head:hp [dir=both] #newnode:newp -> prev:prn [dir=both] #} rankdir = LR newnode[label = "{<newp>|node|<newn>}" shape = record] dotp[label = "..." shape = box] prev[label = "{<prp>|n|<prn>}" shape = record] head[label = "{<hp>|head|<hn>}" shape = record] next[label = "{<np>|1|<nn>}" shape = record] dotn[label = "..." shape = box] dotp->prev:prp [dir = both] newnode:newn ->head:hp [dir=both] newnode:newp -> prev:prn [dir=both] prev:prn -> head:hp [dir = both style = "dotted"] head:hn -> next:np [dir = both] next:nn ->dotn [ dir = both] dotp -> dotn [dir = both] } ``` --- ```c= static inline void list_del(struct list_head *node) { struct list_head *next = node->next, *prev = node->prev; next->prev = prev; prev->next = next; } ``` 將 node 在 doubly-linked list 中刪除。 ```graphviz digraph p{ rankdir = LR dotp[label="..." shape = box] prev[label = "{<pp>|prev|<pn>}", shape = record] mainnode[label = "{<np>|node|<nn>}", shape = record] next[label = "{<np>|next|<nn>}", shape = record] dotn[label = "..." shape = box] dotp ->prev:pp [dir=both] prev:pn -> next:np[dir=both] prev:pn -> mainnode:np[dir=both, style="dotted"] mainnode:nn -> next:np [dir=both, style="dotted"] next:nn -> dotn [dir=both] } ``` --- ```c= static inline int list_empty(const struct list_head *head) { return (head->next == head); } ``` 檢查這個 doubly-linked list 中是不是沒有任何 node 。 --- ```c= static inline int list_is_singular(const struct list_head *head) { return (!list_empty(head) && head->prev == head->next); } ``` 檢查這個 doubly-linked list 中是不是只有一個 node 。 --- ```c= static inline void list_splice_tail(struct list_head *list, struct list_head *head) { struct list_head *head_last = head->prev; struct list_head *list_first = list->next, *list_last = list->prev; if (list_empty(list)) return; head->prev = list_last; list_last->next = head; list_first->prev = head_last; head_last->next = list_first; } ``` 將 list 中的 node 接入 head 這個 doubly-linked list 的尾端。 這兩個: ```graphviz digraph p{ rankdir = LR hpre[label="{<p>|Hp|<n>}", shape = record] head[label = "{<p>|head|<n>}", shape = record] hnext[label = "{<p>|Hn|<n>}", shape = record] // hdot[label = "", shape = box] // hdotb[label="" , shape=box] hpre:n -> head:p[dir=both] head:n -> hnext:p [dir=both] hpre:p -> hnext:n[dir=both] // hpre:p->hdot[dir=both] // hnext:n->hdotb[dir=both] } ``` ```graphviz digraph l{ rankdir = LR hpre[label="{<p>|Lp|<n>}", shape = record] head[label = "{<p>|list|<n>}", shape = record] hnext[label = "{<p>|Ln|<n>}", shape = record] // hdot[label = "", shape = box] // hdotb[label="" , shape=box] hpre:n -> head:p[dir=both] head:n -> hnext:p [dir=both] hpre:p -> hnext:n[dir=both] } ``` 會變成: ```graphviz digraph both{ rankdir = LR hp[label="{<p>|Hp|<n>}", shape = record] lp[label="{<p>|Lp|<n>}", shape = record] ln[label="{<p>|Ln|<n>}", shape = record] head[label="{<p>|head|<n>}", shape = record] hn[label="{<p>|Hn|<n>}", shape = record] hp:n->lp:p[dir=both] lp:n->ln:p[dir=both] ln:n->head:p[dir=both] head:n->hn:p[dir=both] hn:n->hp:p[dir=both] } ``` :::warning list 指到的 list_head 被拿掉了! ::: --- ```c= static inline void list_cut_position(struct list_head *head_to, struct list_head *head_from, struct list_head *node) { struct list_head *head_from_first = head_from->next; if (list_empty(head_from)) return; if (head_from == node) { INIT_LIST_HEAD(head_to); return; } head_from->next = node->next; head_from->next->prev = head_from; head_to->prev = node; node->next = head_to; head_to->next = head_from_first; head_to->next->prev = head_to; } ``` 在 head_from 這個 doubly-linked list 中,以 node 為裁切點,把 node 以前的(包含 node 本身) doubly-linked list 存到 head_to 中,而其餘的仍舊待在 head_from 裡面。 :::warning 這邊沒有檢查 list_head *head_to 是否有東西感覺很危險?? ::: 切割前: ```graphviz digraph p{ rankdir=LR head_from[label="{<p>|head_from|<n>}", shape=record] A[label="{<p>|A|<n>}", shape = record] B[label="{<p>|B|<n>}", shape=record] n[label="{<p>|node|<n>}", shape = record] C[label="{<p>|C|<n>}", shape=record] D[label="{<p>|D|<n>}", shape=record] head_from:n->A:p[dir=both] A:n->B:p[dir=both] B:n->n:p[dir=both] n:n->C:p[dir=both] C:n->D:p[dir=both] D:n->head_from:p[dir=both] } ``` 切割後: ```graphviz digraph pp{ rankdir=LR head_from[label="{<p>|head_from|<n>}", shape=record] C[label="{<p>|C|<n>}", shape=record] D[label="{<p>|D|<n>}", shape=record] head_from:n->C:p[dir=both] C:n->D:p[dir=both] D:n->head_from:p[dir=both] } ``` ```graphviz digraph n{ rankdir=LR head_to[label="{<p>|head_to|<n>}", shape = record] A[label="{<p>|A|<n>}", shape = record] B[label="{<p>|B|<n>}", shape=record] n[label="{<p>|node|<n>}", shape = record] head_to:n->A:p[dir=both] A:n->B:p[dir=both] B:n->n:p[dir=both] n:n->head_to:p[dir=both] } ``` --- ```c= #define list_entry(node, type, member) container_of(node, type, member) ``` 用來計算 node 所存在的 structure 的位址。 --- ```c= #define list_first_entry(head, type, member) \ list_entry((head)->next, type, member) ``` 用來計算第一個 node 所存在的 structure 的位址。 --- ```c= #define list_for_each(node, head) \ for (node = (head)->next; node != (head); node = node->next) ``` 跑過這個 linked list 中所有的 node 。 ### q1.h ```c= static list_ele_t *get_middle(struct list_head *list) { struct list_head *fast = list->next, *slow; list_for_each (slow, list) { if (COND1 || COND2) break; fast = fast->next->next; } return list_entry(TTT, list_ele_t, list); } ``` 用來取得 linked list 中間的 node 。 而中間迴圈的中止條件應該是要 fast 在最後一個或是倒數第二個的時候中止。 所以 COND1 和 COND2 分別為 ```fast->next == list``` 和 ```fast->next->next == list``` 。 --- ```c= static void list_merge(struct list_head *lhs, struct list_head *rhs, struct list_head *head) { INIT_LIST_HEAD(head); if (list_empty(lhs)) { list_splice_tail(lhs, head); return; } if (list_empty(rhs)) { list_splice_tail(rhs, head); return; } while (!list_empty(lhs) && !list_empty(rhs)) { char *lv = list_entry(lhs->next, list_ele_t, list)->value; char *rv = list_entry(rhs->next, list_ele_t, list)->value; struct list_head *tmp = strcmp(lv, rv) <= 0 ? lhs->next : rhs->next; list_del(tmp); list_add_tail(tmp, head); } list_splice_tail(list_empty(lhs) ? rhs : lhs, head); } ```