# 2019q1 Homework1 (lab0)
contributed by < `shihyuuuuuuu` >
## 題目要求
### 預期目標
- 英文閱讀
- C 語言程式設計基礎
- 學習使用 Git 與 GitHub
- 學習效能分析
- 研究自動測試機制
### 作業概述
本作業的目的主要是複習與練習 C 語言的基本技巧,包括以下幾項:
- Explicit memory management, as required in C.(記憶體管理)
- Creating and manipulating pointer-based data structures.(操作指標相關的資料結構)
- Working with strings.(字串操作練習)
- Enhancing the performance of key operations by storing redundant information in data structures.(透過在結構中儲存一些額外資料來加速一些操作的效能)
- Implementing robust code that operates correctly with invalid arguments, including NULL pointers.(學著處理非法輸入的參數,使程式不致於出錯)
### 實作內容
實作一個queue,並且可以支援 last-in, first-out(LIFO) 和 first-in, first-out(FIFO),
使用 singly-linked list 來完成。
要完成以下各函式:
- q_new: 產生一個新的、空的 queue 。
- q_free: 釋放一個 queue 使用的空間。
- q_insert_head: 從 queue 的 head 插入一個新元素 ( LIFO )。
- q_insert_tail: 從 queue 的 tail 插入一個新元素 ( FIFO )。
- q_remove_head: 從 queue 的 head 移除一個元素。
- q_size: 計算 queue 中的元素個數。
- q_reverse: 反轉 queue 。(不能配置獲釋放任何元素)
## 開發環境
```shell
$ uname -a
Linux DESKTOP-CNBJG9V 4.4.0-17134-Microsoft #523-Microsoft Mon Dec 31 17:49:00 PST 2018 x86_64 x86_64 x86_64 GNU/Linux
$ gcc --version
gcc (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0
```
## 實作過程
### q_new
初始化 queue 的記憶體空間與變數,並回傳指向該 queue 的指標。
當 malloc 失敗時,回傳 NULL 。
```clike=
/*
Create empty queue.
Return NULL if could not allocate space.
*/
queue_t *q_new()
{
queue_t *q = malloc(sizeof(queue_t));
/* If malloc return Null, return NULL. */
if (!q)
return NULL;
q->head = NULL;
q->tail = NULL;
q->size = 0;
return q;
}
```
### q_free
從 queue 的 head 到 tail 依序釋放每個 element 的記憶體空間。
除了釋放每個list_ele_t的空間外,也要記得釋放其所存的字串空間。
```clike=
/* Free all storage used by queue */
void q_free(queue_t *q)
{
if (!q)
return;
if (!(q->head)) {
free(q);
return;
}
list_ele_t *next_p;
list_ele_t *next_next_p;
next_p = q->head;
/* Free the queue elements one by on from head to tail. */
while (next_p) {
next_next_p = next_p->next;
free(next_p->value);
free(next_p);
next_p = next_next_p;
}
free(q);
}
```
### q_insert_head
從 queue 的起始位置插入新的 element。
需特別注意無法配置空間或 queue 為 NULL 的情形。
```cpp=
/*
Attempt to insert element at head of queue.
Return true if successful.
Return false if q is NULL or could not allocate space.
Argument s points to the string to be stored.
The function must explicitly allocate space and copy the string into it.
*/
bool q_insert_head(queue_t *q, char *s)
{
list_ele_t *newh;
/* If queue is Null or fail to malloc, return false. */
if (!q) {
return false;
}
if (!(newh = malloc(sizeof(list_ele_t)))) {
return false;
}
/* Allocate space for the string and copy it. */
if (!(newh->value = malloc(sizeof(char) * (strlen(s) + 1)))) {
free(newh);
return false;
}
strcpy(newh->value, s);
if (q->head == NULL) {
q->tail = newh;
}
q->size++;
newh->next = q->head;
q->head = newh;
return true;
}
```
### q_insert_tail
從 queue 的最末位置插入新的 element。
需特別注意無法配置空間或 queue 為 NULL 的情形。
> 遇到的問題:一開始沒有把最尾端的指標設為NULL,導致測試程式找不到尾端。
> 原本認為 C 語言的指標如果沒有初始化 default 是 NULL ,因此沒有特別初始化。
> 後來在[這份](https://devdocs.io/c/language/pointer)裡面看到:*"To **initialize a pointer to null** or to assign the null value to an existing pointer, **a null pointer constant** (NULL, or any other integer constant with the value zero) **may be used**. static initialization also initializes pointers to their null values."*
```cpp=
/*
Attempt to insert element at tail of queue.
Return true if successful.
Return false if q is NULL or could not allocate space.
Argument s points to the string to be stored.
*/
bool q_insert_tail(queue_t *q, char *s)
{
list_ele_t *newt;
if (!q) {
return false;
}
if (!(newt = malloc(sizeof(list_ele_t)))) {
return false;
}
/* Allocate space for the string and copy it. */
if (!(newt->value = malloc(sizeof(char) * (strlen(s) + 1)))) {
free(newt);
return false;
}
strcpy(newt->value, s);
newt->next = NULL;
q->size++;
q->tail->next = newt;
q->tail = newt;
return true;
}
```
### q_remove_head
移除 queue 的第一個 element ,並將q->head指向第二個元素。
```cpp=
/*
Attempt to remove element from head of queue.
Return true if successful.
Return false if queue is NULL or empty.
If sp is non-NULL and an element is removed, copy the removed string to *sp
(up to a maximum of bufsize-1 characters, plus a null terminator.)
*/
bool q_remove_head(queue_t *q, char *sp, size_t bufsize)
{
list_ele_t *remove_ele;
/* If queue is NULL or queue is empty, return false. */
if (!q || !q_size(q)) {
return false;
}
remove_ele = q->head;
if (sp) {
strncpy(sp, remove_ele->value, bufsize - 1);
sp[bufsize - 1] = '\0';
}
q->size--;
q->head = q->head->next;
free(remove_ele->value);
free(remove_ele);
return true;
}
```
### q_size
透過 queue 的其中一個變數 size 來存放當前 queue 的元素個數。
```cpp=
/*
Return number of elements in queue.
Return 0 if q is NULL or empty
*/
int q_size(queue_t *q)
{
if (!q)
return 0;
return q->size;
}
```
### q_reverse
透過一個一個將 element 的指標反轉,來實現整個 queue 的反轉,
並且不需配置或釋放任何空間
```cpp=
/*
Reverse elements in queue
No effect if q is NULL or empty
*/
void q_reverse(queue_t *q)
{
if (!q || q_size(q) == 0 || q_size(q) == 1)
return;
list_ele_t *prev;
list_ele_t *node;
list_ele_t *next;
prev = q->head;
node = prev->next;
q->tail = prev;
prev->next = NULL;
/* Iterate the list. If reach the last node, let q->head point to it. */
while (node) {
next = node->next;
node->next = prev;
prev = node;
node = next;
}
q->head = prev;
}
```
:::info
上面程式碼有修改過, 下為原本的程式碼。
主要修改為**重新命名冗餘的變數名** ,以及**將不必要的 if 去掉**。
```cpp=
list_ele_t *prev;
list_ele_t *next;
list_ele_t *next_next;
q->tail = q->head;
prev = q->head;
next = q->head->next;
q->head->next = NULL;
while (1) {
next_next = next->next;
next->next = prev;
prev = next;
next = next_next;
if (next == NULL) {
q->head = prev;
break;
}
}
```
:::
## 自動評分系統運作的原理
## `qtest` 的行為與技巧