# 2025q1 Homework2 (quiz1+2) contributed by < PigeonSir > ## w1 測驗 1 運用 "a pointer to a pointer" 技巧操作單向的鏈結串列,其中基本的結構體如下 ```graphviz digraph queue_context { rankdir=TB; node [shape=plaintext]; queue1 [label=< <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0"> <TR><TD COLSPAN="1" BGCOLOR="lightgray"><B> list_t </B></TD></TR> <TR><TD PORT="n">next</TD></TR> </TABLE> >]; queue2 [label=< <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0"> <TR><TD PORT="p" COLSPAN="2" BGCOLOR="lightgray"><B> list_item_t </B></TD></TR> <TR><TD>value</TD><TD PORT="n">next</TD></TR> </TABLE> >]; nullNode [label="NULL", shape=none]; queue1:n -> queue2:p [constraint=false]; queue2:n -> nullNode [weight=0, minlen=2, constraint=false]; } ``` ### insert before ```c static inline void list_insert_before(list_t *h, list_item_t *before, list_item_t *item) { list_item_t **p; for (p = &h->head; *p != before; p = &(*p)->next) ; *p = item; item->next = before; } ``` 利用 p 做為指標的指標,指向當前節點的 next 的地址,對 p 執行 deference 可得到下一個節點的地址 與沒有使用指標的指標技巧的作法比較 : ```c static inline void list_insert_before(list_t *h, list_item_t *before, list_item_t *item) { lsit_item_t *cur, *prev; for (cur = h->head; cur != before; prev = cur, cur = cur->next) ; prev->next = item; item->next = cur; } ``` 使用 "a pointer to a pointer" 技巧後就不需要使用額外的指標紀錄拜訪過的節點 (prev) ### 測試程式 #### my_assert ```c #define my_assert(test, message) \ do { \ if (!(test)) \ return message; \ } while (0) ``` 定義 my_assert 宏用於觀察執行時遭遇的錯誤,這裡有一個疑問是為何要使用到 do while , ### 圖例 要插入 node2 於 node3 以前 ```graphviz digraph g{ rankdir=LR; node [shape=record]; l [label = "{<data>h |<ref>}"]; 1 [label = "{<head>node 1} | {<data>1 |<ref>}"]; 2 [label = "{<head>node 3} |{<data>3 |<ref>}"]; 3 [label = "{<head>node 2} | {<data>2 |<ref>}"]; node [shape=none] p; before; edge[weight=2]; l:ref:c -> 1:head [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; 1:ref:c -> 2:head [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; 2:ref:c -> NULL [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; edge[weight=1]; p -> l:ref:s; edge[weight=1] before -> 2:head:n; { rank=max; 3;} } ``` 走訪鏈結串列直到 `*p == before` 時跳出迴圈,此時 p 紀錄的為 node1 中 next 的地址 ```graphviz digraph g{ rankdir=LR; node [shape=record]; l [label = "{<data>h |<ref>}"]; 1 [label = "{<head>node 1} | {<data>1 |<ref>}"]; 2 [label = "{<head>node 3} |{<data>3 |<ref>}"]; 3 [label = "{<head>node 2} | {<data>2 |<ref>}"]; node [shape=none] p; before; edge[weight=2]; l:ref:c -> 1:head [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; 1:ref:c -> 2:head [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; 2:ref:c -> NULL [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; edge[weight=1]; p -> 1:ref:s; edge[weight=1] before -> 2:head:n; { rank=max; 3;} } ``` 更新 *p (即 node1 的 next) 為 node2 ,最後再將 node3 接上於 node2 後便完成 ```graphviz digraph g{ rankdir=LR; node [shape=record]; l [label = "{<data>h |<ref>}"]; 1 [label = "{<head>node 1} | {<data>1 |<ref>}"]; 3 [label = "{<head>node 3} |{<data>3 |<ref>}"]; 2 [label = "{<head>node 2} | {<data>2 |<ref>}"]; node [shape=none] p; before; null; // NULL 仍然保留作為節點名稱 edge[weight=2]; l:ref:c -> 1:head [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; 1:ref:c -> 2:head [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; 2:ref:c -> 3:head [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; 3:ref:c -> null [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; edge[weight=1]; p -> 1:ref:s; edge[weight=1] before -> 3:head:n; { rank=max; null; } // 這行將 null 設為最右側 } ``` ## W1 測驗 2