# 2023q1 Homework1 (lab0)
contributed by < [CYT701](https://github.com/CYT701/lab0-c) >
## 開發環境
```shell
$ gcc --version
gcc (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0
$ lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Address sizes: 39 bits physical, 48 bits virtual
Byte Order: Little Endian
CPU(s): 4
On-line CPU(s) list: 0-3
Vendor ID: GenuineIntel
Model name: Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz
CPU family: 6
Model: 78
Thread(s) per core: 2
Core(s) per socket: 2
Socket(s): 1
Stepping: 3
CPU max MHz: 2800.0000
CPU min MHz: 400.0000
BogoMIPS: 4800.00
```
---
## 開發過程
### q_new
建立新的佇列並呼叫 `malloc` 動態配置記憶體,依要求此佇列的 `next` 與 `prev` 指標都指向自己,利用 `list.h` 中的 `INIT_LIST_HEAD` 來建立此 queue
```c
/* Create an empty queue */
struct list_head *q_new()
{
struct list_head *q_head = malloc(sizeof(*q_head));
if (!q_head)
return NULL;
INIT_LIST_HEAD(q_head);
return q_head;
}
```
### q_free
`list_for_each_safe` 會從 `l->next` 開始遍歷所有節點,在每個節點都用 `list_del` 將節點從串列中移走並釋放記憶體,最後因為是從 `l->next` 開始遍歷,所以要記得釋放 `l`
:::spoiler
```c
/* Free all storage used by queue */
void q_free(struct list_head *l)
{
if(!l)
return;
struct list_head *cur_node;
struct list_head *safe;
list_for_each_safe(cur_node,safe,l)/*go over nodes from (l->next) to the end of list*/
{
list_del(cur_node);/*remove cur_node from list*/
/*struct list_head *next = cur_node->next;
struct list_head *prev = cur_node->prev;
next->prev = prev;
prev->next = next;*/
free(cur_node);
}
free(l);
}
```
:::
更新為使用 `list_for_each_entry_safe` ,能夠對整個 `element_t` 作改動
```c
/* Free all storage used by queue */
void q_free(struct list_head *l)
{
if (!l)
return;
element_t *cur_node;
element_t *safe;
list_for_each_entry_safe (cur_node, safe, l, list) {
list_del(&cur_node->list); /* remove link of cur_node */
q_release_element(cur_node); /* release cur_node */
}
free(l);
}
```
### q_insert_head / q_insert_tail
~~不確定 `char *s` 是什麼用途,感覺用不到~~
:::spoiler
```c
/* Insert an element at head of queue */
bool q_insert_head(struct list_head *head, char *s)
{
if(!head)
return false;
struct list_head *node = malloc(sizeof(*node));
list_add(node,head);
return true;
}
/* Insert an element at tail of queue */
bool q_insert_tail(struct list_head *head, char *s)
{
if(!head)
return false;
struct list_head *node = malloc(sizeof(*node));
list_add_tail(node,head);
return true;
}
```
:::
已找出問題點, `list_head` 這個結構包含在 `element_t` 中,而 `element_t` 中的 `value` 就是 `char` 型態,所以在插入新元素時應考慮 `element_t` 而非單純的串列,故先新增一個 `new_element` 並取得字串 `s` 的長度存於 `len` 中,再將字串 `s` 複製到 `new_element->value` ,並將 `new_element->list` 加入到 `head` 中
:::spoiler
```c
/* Insert an element at head of queue */
bool q_insert_head(struct list_head *head, char *s)
{
if (!head) {
return false;
}
element_t *new_element = malloc(sizeof(element_t));
new_element->value = malloc(sizeof(s)));
strcpy(new_element->value, s);
list_add(&new_element->list, head);
return true;
}
/* Insert an element at tail of queue */
bool q_insert_tail(struct list_head *head, char *s)
{
if (!head) {
return false;
}
element_t *new_element = malloc(sizeof(element_t));
new_element->value = malloc(sizeof(s));
strcpy(new_element->value, s);
list_add_tail(&new_element->list, head);
return true;
}
```
:::
~~尚未解決的錯誤~~
```
Dangerous function detected in /home/cyt/linux2023/lab0-c/queue.c
46: strcpy(new_element->value, s);
59: strcpy(new_element->value, s);
```
已解決如下
[Common vulnerabilities guide for C programmers](https://security.web.cern.ch/recommendations/en/codetools/c.shtml) 中提到 `strcpy` 並不會檢查暫存器的長度,且可能會覆蓋預期目標地址的連續記憶體空間,這表示 `strcpy` 並不是安全的動作,應使用 `strlcpy` 或 `strncpy`
- [ ] `strlcpy` ==(較推薦的方法)==
* 在 BSD system 中才有定義此函數,上述資料來源有說明如何自行定義
- [ ] `strncpy` (可用,但不一定會以 '\0' 作為字串結尾)
```c
若給定 BUFFER_SIZE = 3, dst[BUFFER_SIZE], src[] = abcdef
使用 strlcpy(dst,src,BUFFER_SIZE) 會得到 dst 為 ab\0
使用 strncpy(dst,src,BUFFER_SIZE) 會得到 dst 為 abc
若給定 BUFFER_SIZE = 10, dst[BUFFER_SIZE], src[] = abcdef
使用 strlcpy(dst,src,BUFFER_SIZE) 會得到 dst 為 abcdef\0
使用 strncpy(dst,src,BUFFER_SIZE) 會得到 dst 為 abcdef\0\0\0\0
```
根據上述,修改程式碼如下,加入了自定義的 `strlcpy` 函數
:::spoiler 測試 malloc failure 時發生錯誤
```c
#ifndef strlcpy
#define strlcpy(dst, src, sz) snprintf((dst), (sz), "%s", (src))
#endif
/* Insert an element at head of queue */
bool q_insert_head(struct list_head *head, char *s)
{
if (!head) {
return false;
}
element_t *new_element = malloc(sizeof(element_t));
new_element->value = malloc(sizeof(s));
strlcpy(new_element->value, s, sizeof(&new_element->value));
list_add(&new_element->list, head);
return true;
}
/* Insert an element at tail of queue */
bool q_insert_tail(struct list_head *head, char *s)
{
if (!head) {
return false;
}
element_t *new_element = malloc(sizeof(element_t));
new_element->value = malloc(sizeof(s));
strlcpy(new_element->value, s, sizeof(&new_element->value));
list_add_tail(&new_element->list, head);
return true;
}
```
:::
```shell
+++ TESTING trace trace-11-malloc:
# Test of malloc failure on insert_head
Segmentation fault occurred. You dereferenced a NULL or invalid pointer--- trace-11-malloc 0/6
+++ TESTING trace trace-12-malloc:
# Test of malloc failure on insert_tail
Segmentation fault occurred. You dereferenced a NULL or invalid pointer--- trace-12-malloc 0/6
```
由於這兩筆測資是在測試 malloc failure ,而我的程式碼中並未對 malloc 失敗的情況進行處理,所以在每次動態配置記憶體時,如果發生錯誤就會產生 segmentation fault ,於是在每次動態配置記憶體時都加入了判斷條件檢查是否成功,即可通過測資
:::spoiler 字串並未完全加入佇列中
```c
#ifndef strlcpy
#define strlcpy(dst, src, size) snprintf((dst), (size), "%s", (src))
#endif
/* Insert an element at head of queue */
bool q_insert_head(struct list_head *head, char *s)
{
if (!head) {
return false;
}
element_t *new_element = malloc(sizeof(element_t));
if (!new_element) {
return false;
}
new_element->value = malloc(sizeof(s));
if (!new_element->value) {
return false;
}
strlcpy(new_element->value, s, sizeof(&new_element->value));
list_add(&new_element->list, head);
return true;
}
/* Insert an element at tail of queue */
bool q_insert_tail(struct list_head *head, char *s)
{
if (!head) {
return false;
}
element_t *new_element = malloc(sizeof(element_t));
if (!new_element) {
return false;
}
new_element->value = malloc(sizeof(s));
if (!new_element->value) {
return false;
}
strlcpy(new_element->value, s, sizeof(&new_element->value));
list_add_tail(&new_element->list, head);
return true;
}
```
:::
```shell
+++ TESTING trace trace-07-string:
# Test of truncated strings
ERROR: Removed value aardvar != expected value aardvark_bear_dolphin_gerbil_jaguar
ERROR: Removed value meerkat != expected value meerkat_panda_squirrel_vulture_wolf
ERROR: Removed value meerkat != expected value meerkat_panda_squirrel_vulture
ERROR: Removed value aardvar != expected value aardvark_bear_dolphin_gerbil
ERROR: Removed value aardvar != expected value aardvark_bear_dolphin
Error limit exceeded. Stopping command execution
--- trace-07-string 0/6
```
這邊的問題出在 `new_element->value = malloc(sizeof(s));` 這行,==因為如果直接動態配置 `sizeof(s)` 則只會配置 `char` 的大小 8 給 `new_element->value`== ,又因為使用 `strlcpy` 所以字串的最後一個字元會是 `\0` ,故實際印出的只有 7 個字元
修改方法則是先宣告一個 `int` 變數 `length` ,並利用 `strlen` 取得 `s` 的字串長度,再利用 `new_element->value = malloc(sizeof(char) *length);` 來動態配置
:::spoiler 並非以 constant time 實作
```c
#ifndef strlcpy
#define strlcpy(dst, src, size) snprintf((dst), (size), "%s", (src))
#endif
/* Insert an element at head of queue */
bool q_insert_head(struct list_head *head, char *s)
{
if (!head) {
return false;
}
element_t *new_element = malloc(sizeof(element_t));
if (!new_element) {
return false;
}
int length = strlen(s) + 1;
new_element->value = malloc(sizeof(char) *length);
if (!new_element->value) {
free(new_element);
return false;
}
strlcpy(new_element->value, s, length);
list_add(&new_element->list, head);
return true;
}
/* Insert an element at tail of queue */
bool q_insert_tail(struct list_head *head, char *s)
{
if (!head) {
return false;
}
element_t *new_element = malloc(sizeof(element_t));
if (!new_element) {
return false;
}
int length = strlen(s) + 1;
new_element->value = malloc(sizeof(char) *length);
if (!new_element->value) {
free(new_element);
return false;
}
strlcpy(new_element->value, s, length);
list_add_tail(&new_element->list, head);
return true;
}
```
:::
猜測這裡出現的問題是 `strlen` 的時間複雜度為 `O(n)` ,導致無法以常數時間執行 insert
:::danger
尚未想到解法,參考其他同學的作法,有些人使用 `strdup` 複製字串,可以自動配置記憶體,說不定就能避免這個情況
如果要使用 `strlcpy` 似乎就一定要先計算出字串長度,待補充
:::
### q_remove_head / q_remove_tail
一開始沒有檢查 `head` 是否為空,但後來發現如果有對空佇列進行操作時會無法處理,於是在函式開始時加上檢查條件
建立 `rm_element` ,利用 `list_first_entry` 或 `list_last_entry` 來取得所要求的元素,利用 `list_del` 將其從佇列中移走(在 `queue.h` 中有強調在這裡只需要 unlink 不需要 free ),再利用之前定義好的 `strlcpy` 將 `rm_element->value` 複製到 `sp`
```c
/* Remove an element from head of queue */
element_t *q_remove_head(struct list_head *head, char *sp, size_t bufsize)
{
if (!head || list_empty(head))
return NULL;
element_t *rm_element = list_first_entry(head, element_t, list);
list_del(&rm_element->list);
if (sp) {
strlcpy(sp, rm_element->value, bufsize);
}
return rm_element;
}
/* Remove an element from tail of queue */
element_t *q_remove_tail(struct list_head *head, char *sp, size_t bufsize)
{
if (!head || list_empty(head))
return NULL;
element_t *rm_element = list_last_entry(head, element_t, list);
list_del(&rm_element->list);
if (sp) {
strlcpy(sp, rm_element->value, bufsize);
}
return rm_element;
}
```
### q_size
利用 `list_for_each` 走訪所有節點,每走過一個節點就讓 `count++` ,回傳 `count`
```c
/* Return number of elements in queue */
int q_size(struct list_head *head)
{
if (!head)
return 0;
int count = 0;
struct list_head *cur_node;
list_for_each (cur_node, head)
count = count + 1;
return count;
}
```
### q_delete_mid
想法很單純,就是利用前面做的 `q_size` 來計算出 `mid` ~~,由於是刪除中間的元素,且如果有偶數個元素時取其下界,奇數個就刪除中間的元素~~
:::spoiler 題意理解有誤
```c
/* Delete the middle node in queue */
bool q_delete_mid(struct list_head *head)
{
// https://leetcode.com/problems/delete-the-middle-node-of-a-linked-list/
if (!head || list_empty(head))
return false;
int mid = q_size(head) / 2 + q_size(head) % 2;
int count = 0;
struct list_head *cur_node;
struct list_head *safe;
list_for_each_safe (cur_node, safe, head) {
count = count + 1;
if (count == mid) {
list_del(cur_node);
q_release_element(list_entry(cur_node, element_t, list));
return true;
}
}
return false;
}
```
:::
打一半突然覺得怪怪的,回去看 `queue.h` 發現題目是要求用 `0-based indexing` ,並取第 ⌊n / 2⌋ 個元素為中間,所以我是用錯誤的計算方式但不小心算出正確答案,程式碼應修正如下
```c
/* Delete the middle node in queue */
bool q_delete_mid(struct list_head *head)
{
// https://leetcode.com/problems/delete-the-middle-node-of-a-linked-list/
if (!head || list_empty(head))
return false;
int mid = (q_size(head) - 1) / 2; /*middle of 0-based indexing*/
int count = -1; /*count 0 at first node*/
struct list_head *cur_node;
struct list_head *safe;
list_for_each_safe (cur_node, safe, head) {
count = count + 1;
if (count == mid) {
list_del(cur_node);
q_release_element(list_entry(cur_node, element_t, list));
return true;
}
}
return false;
}
```
### q_delete_dup
利用 `strcmp` 檢查 `cur_node` 和 `safe` 是否相同,若相同則刪除 `cur_node` ,由於 `list_for_each_entry_safe` 的定義,接下來 `cur_node = safe, safe = cur_node->next`,所以不會因為刪除 `cur_node` 而導致錯誤
:::spoiler 無法正確刪除元素
```c
/* Delete all nodes that have duplicate string */
bool q_delete_dup(struct list_head *head)
{
// https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/
if (!head || list_empty(head))
return false;
element_t *cur_node;
element_t *safe;
list_for_each_entry_safe (cur_node, safe, head, list) {
if (strcmp(cur_node->value, safe->value) == 0) {
list_del(&cur_node->list);
q_release_element(cur_node);
}
}
return true;
}
```
:::
上述程式碼出現的問題是,由於題目的要求是只要字串重複的節點就要==一個不留的刪除==,而不是保留其中一個,所以在原本的程式碼中加入了布林值 `is_dup` 用以記錄 `cur_node` 是否為重複的字串
:::spoiler 出現 segmentation fault
```c
/* Delete all nodes that have duplicate string */
bool q_delete_dup(struct list_head *head)
{
// https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/
if (!head || list_empty(head))
return false;
bool is_dup = false; /*check if cur_node is duplicate*/
element_t *cur_node;
element_t *safe;
list_for_each_entry_safe (cur_node, safe, head, list) {
if (strcmp(cur_node->value, safe->value) == 0) {
is_dup = true;
list_del(&cur_node->list);
q_release_element(cur_node);
} else if (is_dup) { /*cur_node is duplicate, delete it*/
is_dup = false;
list_del(&cur_node->list);
q_release_element(cur_node);
}
}
return true;
}
```
:::
發現問題出在使用 `strcmp` 檢查 `cur_node` 與 `safe` 之前,應先檢查 `safe` 是否已經走到 `head` ,否則直接使用 `safe->value` 會導致 segmentation fault
```c
/* Delete all nodes that have duplicate string */
bool q_delete_dup(struct list_head *head)
{
// https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/
if (!head || list_empty(head))
return false;
bool is_dup = false; /*check if cur_node is duplicate*/
element_t *cur_node;
element_t *safe;
list_for_each_entry_safe (cur_node, safe, head, list) {
if (&safe->list != head && strcmp(cur_node->value, safe->value) == 0) {
is_dup = true;
list_del(&cur_node->list);
q_release_element(cur_node);
} else if (is_dup) { /*cur_node is duplicate, delete it*/
is_dup = false;
list_del(&cur_node->list);
q_release_element(cur_node);
}
}
return true;
}
```
### q_swap
~~利用 `list_for_each_safe` 遍歷整個佇列,每次都將 `cur_node` 移動到 `safe` 後面即可完成交換~~
:::spoiler swap 結果不如預期
```c
void q_swap(struct list_head *head)
{
// https://leetcode.com/problems/swap-nodes-in-pairs/
if (!head)
return;
struct list_head *cur_node;
struct list_head *save;
list_for_each_safe (cur_node, save, head) {
list_move(cur_node, save);
}
}
```
:::
在檢查的時候發現這樣做會出現問題,因為在 `list_for_each_safe` 中,往下一個節點前進時使用了 `cur_node = safe, safe = cur_node->next` ,但是在這之前由於做了 `list_move(cur_node, save)` ,也就是 `cur_node` 會在 `safe->next` ,此時如果作 `cur_node = safe, safe = cur_node->next` ,則會把 `safe` 與 `cur_node` 對調,不能達到原本預期的兩兩交換效果
故調整 `list_for_each_safe` 的寫法,將原本 `cur_node = safe, safe = cur_node->next` 改為 `cur_node = cur_node->next, safe = cur_node->next`
:::spoiler swap 結果不如預期
```c
/* Swap every two adjacent nodes */
void q_swap(struct list_head *head)
{
// https://leetcode.com/problems/swap-nodes-in-pairs/
if (!head)
return;
struct list_head *cur_node;
struct list_head *safe;
for (cur_node = (head)->next, safe = cur_node->next; cur_node != (head);
cur_node = cur_node->next, safe = cur_node->next) {
list_move(cur_node, safe);
}
}
```
:::
在使用 `./qtest` 檢查時發現結果仍然不如預期,且只有在奇數個元素時才會出現問題,發現問題出在 for 迴圈中判斷迴圈終止條件時只判斷了 `cur_node != head` ,應加入 `safe != head` 才能夠正確判斷
```c
/* Swap every two adjacent nodes */
void q_swap(struct list_head *head)
{
// https://leetcode.com/problems/swap-nodes-in-pairs/
if (!head)
return;
struct list_head *cur_node;
struct list_head *safe;
for (cur_node = (head)->next, safe = cur_node->next;
cur_node != (head) && safe != (head);
cur_node = cur_node->next, safe = cur_node->next) {
list_move(cur_node, safe);
}
}
```
### q_reverse
利用 `list_for_each_safe` 走訪所有節點,每次都將當前節點移動到 `head`,即可完成反轉
```c
/* Reverse elements in queue */
void q_reverse(struct list_head *head)
{
if (!head)
return;
struct list_head *cur_node;
struct list_head *safe;
list_for_each_safe (cur_node, safe, head) {
list_move(cur_node, head);
}
}
```
### q_reverseK
反轉方式與上述相同,加入了 `count` 來計算走過得節點數,達到 k 時使用 `list_cut_position` ,將佇列切開後利用前面實作的 `q_reverse` 反轉,再接回原本的位置上
```c
/* Reverse the nodes of the list k at a time */
void q_reverseK(struct list_head *head, int k)
{
// https://leetcode.com/problems/reverse-nodes-in-k-group/
if (!head || list_empty(head))
return;
LIST_HEAD(temp_head);
struct list_head *cur_node;
struct list_head *safe;
struct list_head *from = head;
int count = 0;
list_for_each_safe (cur_node, safe, head) {
count = count + 1;
if (count == k) {
list_cut_position(&temp_head, from, cur_node);
q_reverse(&temp_head);
list_splice_init(&temp_head, from);
count = 0;
from = safe->prev;
}
}
}
```
### q_sort
根據 [你所不知道的 C 語言: linked list 和非連續記憶體](https://hackmd.io/@sysprog/c-linked-list#Merge-Sort-%E7%9A%84%E5%AF%A6%E4%BD%9C)中對於 merge sort 的實作,以及 [Risheng1128](https://hackmd.io/kZtEa_LdSGKVQGg8jczrLQ?view#2022q1-Homework1-lab0) , [seasonwang0905](https://hackmd.io/@seasonwang/B1B0lVqpo) , [Lichiiiii](https://hackmd.io/@NYC6Z-WqQ3W-61xcE-2SvA/SJDBv7Cps#q_sort) 的程式碼
使用三個 function 來完成
:::spoiler
```c
/* Sort elements of queue in ascending order */
struct list_head *merge_two_list(struct list_head *l1, struct list_head *l2)
{
struct list_head *temp = NULL;
struct list_head **indirect = &temp;
for (struct list_head **node = NULL; l1 && l2; *node = (*node)->next) {
element_t *e1 = list_entry(l1, element_t, list);
element_t *e2 = list_entry(l2, element_t, list);
if (strcmp(e1->value, e2->value) < 0)
node = &l1;
else
node = &l2;
*indirect = *node;
indirect = &(*indirect)->next;
}
*indirect = (struct list_head *) ((u_int64_t) l1 | (u_int64_t) l2);
return temp;
}
struct list_head *merge_sort(struct list_head *head)
{
if (!head || !head->next) {
return head;
}
struct list_head *slow = head;
for (struct list_head *fast = head->next; fast && fast->next;
fast = fast->next->next) {
slow = slow->next;
}
struct list_head *mid;
mid = slow->next;
slow->next = NULL;
struct list_head *left = merge_sort(head);
struct list_head *right = merge_sort(mid);
return merge_two_list(left, right);
}
void q_sort(struct list_head *head)
{
if (!head || list_empty(head))
return;
head->prev->next = NULL;
head->next = merge_sort(head->next);
struct list_head *current = head, *next = head->next;
while (next) {
next->prev = current;
current = next;
next = next->next;
}
current->next = head;
head->prev = current;
}
```
:::
### q_descend
參照 `list_for_each_entry_safe` 的寫法,由 `head->prev` 開始反向檢查,每當 `cur_element` 大於 `prev_element` 時,就將 `prev_element` 刪除,但是如果直接刪除 `prev_element` 的話會導致 for 迴圈出現錯誤,所以在迴圈中以 `temp` 暫存 `prev_element` ,將 `prev_element` 指向 `cur_element` ,再刪除 `temp`
:::spoiler Segmentation fault
```c
/* Remove every node which has a node with a strictly greater value anywhere to
* the right side of it */
int q_descend(struct list_head *head)
{
// https://leetcode.com/problems/remove-nodes-from-linked-list/
if (!head) {
return 0;
}
element_t *cur_element;
element_t *prev_element;
for (cur_element = list_entry((head)->prev, element_t, list),
prev_element = list_entry(cur_element->list.prev, element_t, list);
&cur_element->list != (head); cur_element = prev_element,
prev_element = list_entry(prev_element->list.prev, element_t, list)) {
if (strcmp(cur_element->value, prev_element->value) > 0) {
element_t *temp = prev_element;
prev_element = cur_element;
list_del(&temp->list);
q_release_element(temp);
}
}
return q_size(head);
}
```
:::
與 `delete_dup` 的錯誤相同,未檢查 `prev_element` 是否已經走到 `head`,加上判斷條件就正確了
```c
/* Remove every node which has a node with a strictly greater value anywhere to
* the right side of it */
int q_descend(struct list_head *head)
{
// https://leetcode.com/problems/remove-nodes-from-linked-list/
if (!head) {
return 0;
}
element_t *cur_element;
element_t *prev_element;
for (cur_element = list_entry((head)->prev, element_t, list),
prev_element = list_entry(cur_element->list.prev, element_t, list);
&cur_element->list != (head); cur_element = prev_element,
prev_element = list_entry(prev_element->list.prev, element_t, list)) {
if (&prev_element->list != head &&
strcmp(cur_element->value, prev_element->value) > 0) {
element_t *temp = prev_element;
prev_element = cur_element;
list_del(&temp->list);
q_release_element(temp);
}
}
return q_size(head);
}
```
### q_merge
```c
/* Merge all the queues into one sorted queue, which is in ascending order */
int q_merge(struct list_head *head)
{
// https://leetcode.com/problems/merge-k-sorted-lists/
if (!head || list_empty(head)) {
return 0;
}
if (list_is_singular(head)) {
return list_entry(head->next, queue_contex_t, chain)->size;
}
queue_contex_t *merged_list = list_entry(head->next, queue_contex_t, chain);
struct list_head *node = NULL, *safe = NULL;
list_for_each_safe (node, safe, head) {
if (node == head->next) {
continue;
}
queue_contex_t *temp = list_entry(node, queue_contex_t, chain);
list_splice_init(temp->q, merged_list->q);
}
q_sort(merged_list->q);
return merged_list->size;
}
```
### make test 評分結果
:::danger
```
--- TOTAL 95/100
```
:::