# 2024q1 Homework1 (lab0)
contributed by < [wayne10823014](https://github.com/wayne10823014) >
### Reviewed by `weihsinyeh`
1. 邊寫程式碼邊更新開發紀錄,很多程式沒被紀錄進來。
2. [commit 49f4d09](https://github.com/wayne10823014/lab0-c/commit/49f4d09eb9c1a6835f7b296c0b2e650404819815) 143行 `char *maxVal = NULL;` 這個拿掉。解決辦法就是每次 commit 前整理程式碼。
3. [commit 826d272](https://github.com/wayne10823014/lab0-c/commit/826d272ca606f81146c56e4676e640624cc1a497) 寫程式前先看好規則的優點:避免浪費時間。
4. 減少多餘的大括號,判斷式裡面只有一行就可以拿掉。優點無用的大括號一旦減少,那就有更多的行來寫註解。
## 開發環境
```shell
$ gcc --version
(Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
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): 8
On-line CPU(s) list: 0-7
Vendor ID: GenuineIntel
Model name: Intel(R) Core(TM) i5-8265U CPU @ 1.60GHz
CPU family: 6
Model: 142
Thread(s) per core: 2
Core(s) per socket: 4
Socket(s): 1
Stepping: 11
CPU max MHz: 3900.0000
CPU min MHz: 400.0000
BogoMIPS: 3600.00
```
:::warning
將語系設定為 `C`,亦即執行 `export LC_ALL=C`,再執行上方的命令,這樣才會得到英文的訊息。
:::
## 針對佇列操作的程式碼實作
### `q_insert_head`
一開始的程式碼為以下
```c
bool q_insert_head(struct list_head *head, char *s)
{
element_t *node = malloc(sizeof(element_t));
if (!node) {
return false;
}
char *str = malloc(sizeof(char) * strlen(s) + 1);
if (!str) {
return false;
}
strcpy(str, s);
node->value = str;
list_add(&node->list, head);
return true;
}
```
:::danger
無論標題和內文中,中文和英文字元之間要有空白字元 (對排版和文字搜尋有利)
:::
出現了兩個問題
1. strcpy <s>函數</s> 函式被警告為危險函式,建議使用 strncpy 進行替代。 strncpy 允許指定最大複製<s>字符數</s> 位元組的數量,從而避免緩衝區溢出的問題。
2. 在檢查 str 是否成功配置的條件中,遇到 "記憶體洩漏: node" 的問題。發現 str 在 malloc <s>分配</s> 配置時失敗,應該釋放先前為 node 配置的空間。
:::warning
function 一字多義,務必依據場景提供合適的翻譯。對應到數學公式,function 應翻譯為「函數」,後者就像是台「機器」,它能夠將集合 A 裡面的每個元素「唯一地」對應到集合 B 裡的一個元素。但在 C 語言一類的通用程式語言中,function 未必具備數學函數的意涵,也就是缺乏「唯一對應」的特性,例如對於 time 來說,給定同樣的輸入,卻會在不同時間點得到不同的結果,在 C 語言一類的通用程式語言中,甚至可省略傳回值,後者就不符合「函數」的行為。因此,當在通用的程式設計中,function 應翻譯為「函式」,表示「衍生自數學函數的常式 (routine)」。
$\to$ [資訊科技詞彙翻譯](https://hackmd.io/@sysprog/it-vocabulary)
allocate 翻譯為「配置」,而非「分配」,是為了跟「分發」(dispatch) 區隔,二者都會在作業系統領域多次出現。
:::
:::danger
避免過多的中英文混用,已有明確翻譯詞彙者,例如「鏈結串列」(linked list) 和「佇列」(queue),就使用該中文詞彙,英文則留給變數名稱、人名,或者缺乏通用翻譯詞彙的場景。
:::
修正後
```diff
char *str = malloc(sizeof(char) * strlen(s) + 1);
if (!str) {
+ free(node);
return false;
}
- strcpy(str, s);
+ strncpy(str, s,strlen(s) + 1);
node->value = str;
list_add(&node->list, head);
return true;
```
:::warning
使用 diff 工具程式來產生程式碼差異,注意縮排
:::
:::danger
改進你的漢語表達。
:::
### `q_free`
一開始的程式碼為以下
```c
void q_free(struct list_head *l)
{
if (!l) {
return;
}
element_t *entry, *safe;
list_for_each_entry_safe (entry, safe, l, list) {
free(entry);
}
free(l);
}
```
出現 "已釋放佇列,但仍分配 52199562 個區塊" 的錯誤。檢查後發現已釋放 entry 配置的空間,但 entry 內的 value 所使用的空間並未一併釋放。因此需要添加程式碼來釋放 value 所擁有的空間
修正後
```diff
}
element_t *entry, *safe;
list_for_each_entry_safe (entry, safe, l, list) {
+ if (entry->value) {
+ free(entry->value);
+ }
free(entry);
}
free(l);
```
:::warning
參閱 [free(3)](https://man7.org/linux/man-pages/man3/free.3.html) 可知 "If ptr is NULL, no operation is performed.",意味著你不用撰寫形如 `if (entry->value) { free(entry->value); }` 的程式碼,直接呼叫 `free` 即可,不用額外的 `if` 敘述。
:::
根據規格書中
>The free() function frees the memory space pointed to by ptr,which must have been returned by a previous call to malloc() or related functions. Otherwise, or if ptr has already been freed,undefined behavior occurs. If ptr is NULL, no operation is performed.
在 ptr 為 NULL 的情況下,使用 free() 釋放 ptr 不會有任何作用。
修正多餘的 if 判斷式
```diff
}
element_t *entry, *safe;
list_for_each_entry_safe (entry, safe, l, list) {
- if (entry->value) {
- free(entry->value);
- }
+ free(entry->value);
free(entry);
}
free(l);
```
### `q_delete_mid`
一開始的程式碼為以下
```c
bool q_delete_mid(struct list_head *head)
{
if (!head || list_empty(head)) {
return false;
}
struct list_head *node = head;
for (struct list_head *fast = head; fast && fast->next;
fast = fast->next->next) {
node = node->next;
}
element_t *tmp = list_entry(node, element_t, list);
list_del(node);
free(tmp->value);
free(tmp);
return true;
}
```
出現 "錯誤:時間限制已超過。可能是你的程式碼進入了無限迴圈,或者你的程式碼效率不足" 。發現因為題目是雙向鏈結串列,若繼續使用原本在 LeetCode 上的寫法,會造成 for 迴圈永遠無法停止。同時,修正了 fast 與 node 初始位置設定錯誤造成的問題。
修正後
```diff
if (!head || list_empty(head)) {
return false;
}
- struct list_head *node = head;
- for (struct list_head *fast = head; fast && fast->next;
+ struct list_head *node = head->next;
+ for (struct list_head *fast = head->next;
+ fast != head->prev && fast->next != head->prev;
fast = fast->next->next) {
node = node->next;
}
```
:::warning
善用 List API,撰寫更精簡的程式碼。
:::
### `q_sort`