--- tags: linux2022 --- # 2022q1 Homework1 (lab0) contributed by < [jackyyeh5111](https://github.com/laneser) > [共筆示範](https://hackmd.io/@cccccs100203/linux2020-lab0) ## 實驗環境 ```shell $ gcc --version gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0 $ lscpu Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian Address sizes: 39 bits physical, 48 bits virtual CPU(s): 6 On-line CPU(s) list: 0-5 Thread(s) per core: 1 Core(s) per socket: 6 Socket(s): 1 NUMA node(s): 1 Vendor ID: GenuineIntel CPU family: 6 Model: 158 Model name: Intel(R) Core(TM) i5-9400F CPU @ 2.90GHz Stepping: 10 CPU MHz: 800.125 CPU max MHz: 4100.0000 CPU min MHz: 800.0000 BogoMIPS: 5799.77 Virtualization: VT-x L1d cache: 192 KiB L1i cache: 192 KiB L2 cache: 1.5 MiB L3 cache: 9 MiB NUMA node0 CPU(s): 0-5 ``` ## [作業要求](https://hackmd.io/@sysprog/linux2022-lab0) [queue.c](https://github.com/sysprog21/lab0-c/blob/master/queue.c) 僅提供介面 ([interface](https://en.wikipedia.org/wiki/Interface-based_programming)) 但尚未有完整程式實作,需要由學員補完並逐步精進,包含以下: - `q_new`: 建立新的「空」佇列; - `q_free`: 釋放佇列所佔用的記憶體; - `q_insert_head`: 在佇列開頭 (head) 插入 (insert) 給定的新節點 (以 LIFO 準則); - `q_insert_tail`: 在佇列尾端 (tail) 插入 (insert) 給定的新節點 (以 FIFO 準則); - `q_remove_head`: 在佇列開頭 (head) 移去 (remove) 給定的節點; - `q_release_element`: 釋放特定節點的記憶體空間; - `q_size`: 計算目前佇列的節點總量; - `q_delete_mid`: 移走佇列中間的節點,詳見 [LeetCode 2095. Delete the Middle Node of a Linked List](https://leetcode.com/problems/delete-the-middle-node-of-a-linked-list/) - `q_delete_dup`: 在已經排序的狀況,移走佇列中具備重複內容的節點,詳見 [LeetCode 82. Remove Duplicates from Sorted List II](https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/) - `q_swap`: 交換佇列中鄰近的節點,詳見 [LeetCode 24. Swap Nodes in Pairs](https://leetcode.com/problems/swap-nodes-in-pairs/) - `q_reverse`: 以反向順序重新排列鏈結串列,該函式不該配置或釋放任何鏈結串列節點,換言之,它只能重排已經存在的節點; - `q_sort`: 以遞增順序來排序鏈結串列的節點,可參閱 [Linked List Sort](https://npes87184.github.io/2015-09-12-linkedListSort/) 得知實作手法; ## 完成佇列操作 重要術語解釋(提醒自己) - `node`: 資料型態為 `list_head`,每個 `entry` 裡都有 `node`,用以鏈結佇列中各元素,包含每個佇列的 head 也屬於 `node` - `entry`: 佇列中的元素,資料型態為 `element_t`,是一個結構包含 `node`, `value` - `head`: 用以指向佇列中第一個和最後一個元素的 `node`,若佇列為空則指向自己 ==TODO== 說明 `element_t` 包含`list_head` 的好處 ### q_new ### q_free ### q_insert_head ```c= bool q_insert_head(struct list_head *head, char *s) { if (!head) return false; element_t *ele = malloc(sizeof(element_t)); if (!ele) return false; ele->value = malloc((strlen(s) + 1) * sizeof(char)); if (!ele->value) { q_release_element(ele); return false; } strncpy(ele->value, s, strlen(s) + 1); list_add(&(ele->list), head); // 導致 Memory leak? return true; } ``` 上方程式可以順利編譯,並於 make check 時也顯示正常輸出。但是在提交時,被靜態分析指出有 memory leak 風險 ![](https://i.imgur.com/4trMGfi.png) :::danger 文字訊息不要用圖片展現! :notes: jserv ::: 若把上方程式第 17 行的取址後的括弧拿掉如下圖,即可解決此問題,為什麼呢? (還在找資料中) :::danger 「找出問題」和「找資料」是不同的,前者要思考和試驗,後者是靠 Google 賞賜,你到底投入哪一項? :notes: jserv ::: ```c bool q_insert_head(struct list_head *head, char *s) { if (!head) return false; element_t *ele = malloc(sizeof(element_t)); if (!ele) return false; ele->value = malloc((strlen(s) + 1) * sizeof(char)); if (!ele->value) { q_release_element(ele); return false; } strncpy(ele->value, s, strlen(s) + 1); list_add(&ele->list, head); // 替換掉 &(ele->list) return true; } ``` ### q_insert_tail ### q_remove_head ### q_release_element ### q_size ### q_delete_mid ### q_delete_dup 一開始誤解此題意思,以為將重複的元素刪除至剩下一個即可,後來仔細看才發現只要元素有重複,必須將其相關的節點全數刪除 :::spoiler 錯誤程式碼 ```c bool q_delete_dup(struct list_head *head) { if (!head) return false; element_t *ele, *safe; list_for_each_entry_safe (ele, safe, head, list) { if (&safe->list == head) break; if (!strcmp(ele->value, safe->value)) list_del(&ele->list); } return true; } ``` ::: 註解中有說到:`this function always be called after sorting` :::spoiler 實際程式碼 ```c bool q_delete_dup(struct list_head *head) { if (!head || list_empty(head) || list_is_singular(head)) return false; struct list_head *cur = head->next, *next = cur->next; while (cur != head && next != head) { element_t *cur_entry = list_entry(cur, element_t, list); element_t *next_entry = list_entry(next, element_t, list); bool is_dup = false; // Do this way because list is guaranteed to be sorted in ascending // order. while (next != head && !strcmp(cur_entry->value, next_entry->value)) { list_del(next); q_release_element(next_entry); next = cur->next; next_entry = list_entry(next, element_t, list); is_dup = true; } if (is_dup) { list_del(cur); q_release_element(cur_entry); is_dup = false; } cur = next; next = cur->next; } return true; } ``` ::: ### q_swap ### q_reverse :::spoiler 程式碼 ```c void q_reverse(struct list_head *head) { if (!head || list_empty(head)) return; struct list_head *cur = head->next, *next = cur->next; while (cur != head) { cur->next = cur->prev; cur->prev = next; cur = next; next = cur->next; } cur->next = cur->prev; cur->prev = next; } ``` ::: ### q_sort