# quene.c開發紀錄
[github](https://github.com/eason9999/lab0-c)
## 開發環境
```
$ lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Address sizes: 46 bits physical, 48 bits virtual
Byte Order: Little Endian
CPU(s): 24
On-line CPU(s) list: 0-23
Vendor ID: GenuineIntel
Model name: 13th Gen Intel(R) Core(TM) i7-13700
CPU family: 6
Model: 183
Thread(s) per core: 2
Core(s) per socket: 16
Socket(s): 1
Stepping: 1
CPU(s) scaling MHz: 18%
CPU max MHz: 5200.0000
CPU min MHz: 800.0000
BogoMIPS: 4224.00
Virtualization features:
Virtualization: VT-x
Caches (sum of all):
L1d: 640 KiB (16 instances)
L1i: 768 KiB (16 instances)
L2: 24 MiB (10 instances)
L3: 30 MiB (1 instance)
NUMA:
NUMA node(s): 1
NUMA node0 CPU(s): 0-23
```
## 補全queue.c
### q_new
一開始想先了解linux kernel的寫法(因為看不懂)
先研讀queue.h和list.h相關程式碼發現INIT_LIST_HEAD()這個函式可以使程式碼更簡潔
```c
/* Create an empty queue */
struct list_head *q_new()
{
struct list_head *head = malloc(sizeof(*head));
if (!head)
return NULL;
INIT_LIST_HEAD(head);
return head;
}
```
INIT_LIST_HEAD(head)會初始化head指向其自身
內部程式碼為
```c
head->next=head;
head->prev=head;
```
如此我們即創建一個新的queue而目前為空佇列
### q_free
在研究list.h時發現有list_for_each_safe(pos, next, head)這個迴圈的控制結構,它的作用是讓 pos 依序指向每個節點,而 next 則確保當 pos 被刪除時,仍然能繼續遍歷下一個節點。
這個宏不會自動刪除節點或釋放記憶體,所以還是需要手動處理刪除與釋放記憶體的步驟
```c
/* Free all storage used by queue */
void q_free(struct list_head *head)
{
if (!head)
return;
struct list_head *pos, *next;
list_for_each_safe(pos, next, head) {
element_t *elem = list_entry(pos, element_t, list);
list_del(pos);
free(elem->value);
free(elem);
}
free(head);
}
```
pos:當前正在處理的節點
next:下一個節點,確保當前節點刪除後不影響迴圈繼續運行
- 為什麼要用 _safe?
Ans : 直接刪除 pos 後,它的 next 可能會無法訪問,因此 list_for_each_safe 事先儲存 next 以確保迴圈不會因為 pos 被刪除而出錯。
``
element_t *elem = list_entry(pos, element_t, list);
``
list_entry 是 Linux Kernel 提供的宏:把 pos 轉換回實際的 element_t 結構(因為 list_head 只是鏈結串列的一部分,而 element_t 是整個節點)。
element_t 內部應該包含:
```
struct element {
char *value;
struct list_head list;
};
```
這樣我們就可以存取 value 和 list。
- list_del(pos);
從鏈結串列中刪除該節點,但還沒有釋放記憶體。
- free(elem->value);
釋放該節點的 value,這是動態配置的字串。
- free(elem);
釋放該節點的記憶體,避免記憶體洩漏。
- free(head);
在遍歷並釋放所有節點後,最後釋放 head 所佔用的記憶體。
這適用於 head 本身是動態配置的情況(如 malloc() 分配的鏈結串列)。
但如果 head 是 stack 上的變數,不應該 free(head),否則會導致錯誤(double free)。
### q_insert_head
```c
/* Insert an element at head of queue */
bool q_insert_head(struct list_head *head, char *s)
{
if (!head || !s)
return false;
element_t *new_node = malloc(sizeof(element_t));
if (!new_node)
return false;
new_node->value = strdup(s);
if (!new_node->value) {
free(new_node);
return false;
}
struct list_head *next = head->next;
head->next = &new_node->list;
new_node->list.next = next;
new_node->list.prev = head;
next->prev = &new_node->list;
return true;
}
```
### q_insert_tail
```c
/* Insert an element at tail of queue */
bool q_insert_tail(struct list_head *head, char *s)
{
if (!head || !s)
return false;
element_t *new_node = malloc(sizeof(element_t));
if (!new_node)
return false;
new_node->value = strdup(s);
if (!new_node->value) {
free(new_node);
return false;
}
struct list_head *prev = head->prev;
head->prev = &new_node->list;
new_node->list.next = head;
new_node->list.prev = prev;
prev->next = &new_node->list;
return true;
}
```
### q_remove_head
```c
/* Remove an element from head of queue */
element_t *q_remove_head(struct list_head *head, char *sp, size_t bufsize)
{
if (!head)
return NULL;
struct list_head *first = head->next;
struct list_head *next = first->next;
head->next = next;
next->prev = head;
element_t *elem = list_entry(first, element_t, list);
if (bufsize > 0) {
strncpy(sp, elem->value, bufsize - 1);
sp[bufsize - 1] = '\0';
}
return elem;
}
```
### q_remove_tail
```c
/* Remove an element from tail of queue */
element_t *q_remove_tail(struct list_head *head, char *sp, size_t bufsize)
{
if (!head)
return NULL;
struct list_head *tail = head->prev;
struct list_head *prev = tail->prev;
prev->next = head;
head->prev = prev;
element_t *elem = list_entry(prev, element_t, list);
if (bufsize > 0) {
strncpy(sp, elem->value, bufsize - 1);
sp[bufsize - 1] = '\0';
}
return elem;
}
```