# 2020q3 Homework1 (lab0)
contributed by < `erickuo5124` >
###### tags: `2020進階電腦系統理論與實作`
## 作業說明
利用 linked list 的各 function 實作,檢閱對 c 語言的認知,作業期望達到以下目標:
- 記憶體管理
- 建立並管理需要透過指標操作的資料結構
- 字串操作
- 改善特定關鍵操作的效能
- 提高程式碼的穩固程度,例如能夠處理無效的參數,包含 NULL 指標
## 環境
```shell
$ uname -a
Linux kuouu 5.4.0-47-generic #51-Ubuntu SMP Fri Sep 4 19:50:52 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
$ gcc --version
gcc (Ubuntu 9.3.0-10ubuntu2) 9.3.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
```
## 實做`queue.c`
### q_new
新增一個新的 queue
```c=
queue_t *q_new()
{
queue_t *q = malloc(sizeof(queue_t));
if (q) {
q->head = NULL;
q->tail = NULL;
q->size = 0;
}
return q;
}
```
必須注意 malloc 失敗時的處理方式
### q_free
將 queue 的記憶體釋放掉
```c=
void q_free(queue_t *q)
{
if (!q)
return;
list_ele_t *curr = q->head;
while (curr) {
list_ele_t *next = curr->next;
free(curr->value);
free(curr);
curr = next;
}
free(q);
}
```
除了 q ,還得釋放 q 中每個元素的記憶體
### q_insert_head
在 queue 的頭插入一個元素
```c=
bool q_insert_head(queue_t *q, char *s)
{
if (!q)
return false;
list_ele_t *newh = malloc(sizeof(list_ele_t));
if (!newh)
return false;
newh->value = malloc(sizeof(char) * (strlen(s) + 1));
if (!newh->value) {
free(newh);
return false;
}
memset(newh->value, '\0', strlen(s) + 1);
strncpy(newh->value, s, strlen(s));
newh->next = q->head;
q->head = newh;
if (!q->tail)
q->tail = newh;
q->size++;
return true;
}
```
得注意每次 malloc 時有沒有成功,以及複製字串時,須先將所需空間+1的位址全設為'\0',最後再將新的元素加到 q 的頭。
若 q 的大小為0時,則須將該元素同時指定為 head 跟 tail
### q_insert_tail
在 queue 的結尾插入一個元素
```c=
bool q_insert_tail(queue_t *q, char *s)
{
if (!q)
return false;
list_ele_t *newt = malloc(sizeof(list_ele_t));
if (!newt)
return false;
newt->value = malloc(sizeof(char) * (strlen(s) + 1));
if (!newt->value) {
free(newt);
return false;
}
memset(newt->value, '\0', strlen(s) + 1);
strncpy(newt->value, s, strlen(s));
newt->next = NULL;
if (q->head)
q->tail->next = newt;
else
q->head = newt;
q->tail = newt;
q->size++;
return true;
}
```
注意的地方同 q_insert_head。
### q_remove_head
移除 queue 開頭的元素
```c=
bool q_remove_head(queue_t *q, char *sp, size_t bufsize)
{
if (!q || !q->head)
return false;
list_ele_t *rm = q->head;
if (sp) {
memset(sp, '\0', bufsize);
strncpy(sp, rm->value, bufsize - 1);
}
free(rm->value);
q->head = q->head->next;
free(rm);
q->size--;
if (q->size)
q->tail = NULL;
return true;
}
```
若是 q 不存在或大小為 1 ,則須回傳 false 。
在複製字串時一樣需要先全設為'\0',並在釋放記憶體後將頭指到正確的位置。
### q_size
回傳 queue 的大小
```c=
int q_size(queue_t *q)
{
return q ? q->size : 0;
}
```
若 q 不存在,則須回傳0
### q_reverse
反轉 queue
```c=
void q_reverse(queue_t *q)
{
if (!q)
return;
list_ele_t *curr = q->head;
list_ele_t *last = NULL;
list_ele_t *next = NULL;
q->tail = curr;
while (curr) {
next = curr->next;
curr->next = last;
last = curr;
curr = next;
}
q->head = last;
}
```
從頭開始迭代,並從尾巴開始建立新的 queue
### q_sort
對 queue 作排序
```c=
void q_sort(queue_t *q)
{
if (!q || !q->head)
return;
q->head = merge_sort(q->head);
return;
}
```
```c=
list_ele_t *merge_sort(list_ele_t *head)
{
// merge sort
if (!head || !head->next)
return head;
list_ele_t *fast = head->next;
list_ele_t *slow = head;
// split list
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
}
fast = slow->next;
slow->next = NULL;
// sort each list
list_ele_t *l1 = merge_sort(head);
list_ele_t *l2 = merge_sort(fast);
list_ele_t *merge = NULL;
// merge sorted l1 and sorted l2
while (l1 && l2) {
if (strcasecmp(l1->value, l2->value) <= 0) {
if (!merge) {
head = merge = l1;
} else {
merge->next = l1;
merge = merge->next;
}
l1 = l1->next;
} else {
if (!merge) {
head = merge = l2;
} else {
merge->next = l2;
merge = merge->next;
}
l2 = l2->next;
}
}
if (l1)
merge->next = l1;
if (l2)
merge->next = l2;
return head;
}
```
與作業中 merge sort 的[連結](https://npes87184.github.io/2015-09-12-linkedListSort/)實作方法相同,差在比大小時須注意第一個 merge 的元素須特別處理。
## 運用 Valgrind 排除 qtest 實作的記憶體錯誤
```bash
$ valgrind -q --leak-check=full ./qtest -f traces/trace-13-complexity.cmd
ERROR: Could not open source file 'traces/trace-13-complexity.cmd'
==4523== 32 bytes in 1 blocks are still reachable in loss record 1 of 24
==4523== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==4523== by 0x10C636: malloc_or_fail (report.c:189)
==4523== by 0x10D1B7: add_cmd (console.c:123)
==4523== by 0x10D2A0: init_cmd (console.c:93)
==4523== by 0x10BE31: main (qtest.c:759)
==4523==
.
.
.
```
使用 valgrind 發現從第13筆測資會出問題,但是目前還不知道如何解決