2018q3 HomeWork2 (lab0)
===
contributed by <[LiuJuiHung](https://github.com/LiuJuiHung)>
---
- [ ] 英文閱讀
- [ ] 準備 GNU/Linux 開發工具
- [ ] 學習使用 Git 與 GitHub
- [ ] 學習效能分析
- [ ] 研究自動測試機制
## 英文閱讀
[C Programing Lab](http://www.cs.cmu.edu/~213/labs/cprogramminglab.pdf)
## 學習使用 Git 與 GitHub
1. [GitHub 使用方法](http://wiki.csie.ncku.edu.tw/github)
2. 先建立出自己的 SSH key , 在 terminal 輸入以下指令
```
$ ssh-keygrn -t rsa -C "ray9956666@gmail.com"
$ ssh-agent -s
$ ssh-add ~/.ssh/id_rsa
```
3. 將電腦上的 SSH key (輸入以下指令)複製到 GitHub 的 SSH key 裡
```
$ cat ~/.ssh/id_rsa.pub
```
![](https://i.imgur.com/VRwwdbr.png)
4. 在 terminal 中輸入以下指令驗證
```
$ ssh -T git@github.com
```
![](https://i.imgur.com/WNJGPDb.png)
5. 本機安裝 git
6. [初次設定 Git](https://git-scm.com/book/zh-tw/v1/%E9%96%8B%E5%A7%8B-%E5%88%9D%E6%AC%A1%E8%A8%AD%E5%AE%9A-Git)
* `git config --global user.name "your_name"` 設定使用者名稱
* `git config --global user.email yourmail@example.com` 設定 email
7. 將老師提供的 lab0-c 作業 fork 到自己的 github
8. 在 terminal 輸入以下指令,將作業 git clone 下來
```
$ git clone git@github.com:LiuJuiHung/lab0-c.git
```
9. 程式碼更改後
* `git add .` 加入更改過的全部的檔案
* `git commit` 撰寫 commit message
* `git push` 將檔案 push 到 github 上
```
$ git add .
$ git commit
$ git push
```
---
### Makefile
了解 Makefile 運作
```C=1
CC = gcc
CFLAGS = -O0 -g -Wall -Werror
```
* CC 表示使用 gcc
* CFLAGS:宣告使用的參數
* -O:表示最佳化的程度
* -O 預設就是 -O1,你可以指定成 -O2 或 -O3 ,數字越大表示最佳化程度越好,但是會增加編譯時間
* -g:編入偵錯資訊
* 當使用 GDB 軟體進行偵錯,必須加入 -g 使 GDB 能夠讀取。一般情況下可以不用 -g,因為他也會增加 binary 大小
* -Wall:顯示警告訊息
* 使用這個參數會在編譯時顯示更多的警告訊息
* 這個參數相當有用,特別是找不到 libs/headers 之類的問題
* -Werror:將警告訊息轉為錯誤訊息
* [GCC 常用參數詳解](http://maxubuntu.blogspot.com/2010/02/makefile.html)
---
```C=1
GIT_HOOKS := .git/hooks/applied
all: $(GIT_HOOKS) qtest
```
* all:預設執行的目標,這裡包含 `GIT_HOOKS` 和 `qtest`
---
```C=1
$(GIT_HOOKS):
@scripts/install-git-hooks
@echo
```
* 確認是否有安裝 githooks
---
```C=1
queue.o: queue.c queue.h harness.h
$(CC) $(CFLAGS) -c queue.c
qtest: qtest.c report.c console.c harness.c queue.o
$(CC) $(CFLAGS) -o qtest qtest.c report.c console.c harness.c queue.o
```
* queue.o 的項目裡有 `queue.c`、`queue.h`、 `haeness.h`
* 輸入 `make qtest` 會依照 `CFLAGS` 所定義的參數進行編譯,編譯出一個 `qtest` 執行檔
---
```C=1
test: qtest scripts/driver.py
scripts/driver.py
clean:
rm -f *.o *~ qtest
rm -rf *.dSYM
(cd traces; rm -f *~)
```
* 執行 `make test` 會先查看是否有 `qtest` `scripts/driver.py` 這兩個檔案,若存在則執行 `driver.py` 測試並計算分數
* `clean` 是清除編譯出的 `*.o` `qtest` 和其他生成的檔案
## Queue 開發過程
題目敘述:
1. This program implements a queue supporting both FIFO and LIFO operations
2. It uses a singly-linked list to represent the set of queue elements
### Queue Structure
* 增加 queue 內的總數 size
* 增加 pointer to tail node
```C=1
/* Queue structure */
typedef struct {
list_ele_t *head; /* Linked list of elements */
list_ele_t *tail;
int q_size;
} queue_t;
```
### Create empty queue
* 為一個新的 queue 配置一個記憶體空間,將此空間初始化並回傳
```C=1
queue_t *q_new()
{
queue_t *q = malloc(sizeof(queue_t));
/* What if malloc returned NULL? */
if(!q)
return NULL;
else
{
q->head = NULL;
q->tail = NULL;
q->q_size = 0;
}
return q;
}
```
### Free all storage used by queue
* 主要目的:free 掉整個 queue 的空間
* 在 insert 每個 element 時,都會配置一個記憶體空間,因此利用 while 迴圈將每個 element 的記憶體空間循環的 free 掉
```C=1
void q_free(queue_t *q)
{
/* How about freeing the list elements and the strings? */
/* Free queue structure */
if (!q)
return;
while(q->head)
{
list_ele_t *tmp = q->head;
q->head = tmp->next;
free(tmp);
}
free(q);
}
```
### Insert element at head of queue
* 主要目的:在前端 insert 一個新的 element
* 判斷 queue 是否為 NULL
* 利用 strdup() 將 s 複製到 newh->value
* 判斷為第一筆 element 時,tail pointer 要指向此 element
* q_size 要增加
```C=1
/*
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)
{
if(!q)
return false;
list_ele_t *newh;
newh = malloc(sizeof(list_ele_t));
/* What should you do if the q is NULL? */
if(!newh)
return false;
if(newh)
{
newh->value = strdup(s);
if(q->q_size == 0)
{
q->tail = newh;
newh->next = NULL;
}
else
{
newh->next = q->head;
}
q->head = newh;
q->q_size++;
return true;
}
return false;
}
```
### Attempt to insert element at tail of queue
* 主要目的:在後端 insert 一個新的 element
* 判斷 queue 是否為 NULL
* 利用 strdup() 將 s 複製到 newt->value
* 判斷為第一筆 element 時,head pointer 要指向此 element
* q_size 要增加
```C=1
/*
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.
The function must explicitly allocate space and copy the string into it.
*/
bool q_insert_tail(queue_t *q, char *s)
{
/* You need to write the complete code for this function */
/* Remember: It should operate in O(1) time */
if(!q)
return false;
list_ele_t *newt;
newt = malloc(sizeof(list_ele_t));
if(newt)
{
newt->value = strdup(s);
if(q->q_size == 0)
q->head = newt;
else
{
q->tail->next = newt;
}
newt->next = NULL;
q->tail = newt;
q->q_size++;
return true;
}
return false;
}
```
### Attempt to remove element from head of queue
* 主要目的:刪除前端的 element
* 判斷 sp 是否為 NULL
* 利用 strncpy() 將要被移除的 element 複製到 sp
* 並將此 element free
```C=1
/*
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.)
The space used by the list element and the string should be freed.
*/
bool q_remove_head(queue_t *q, char *sp, size_t bufsize)
{
/* You need to fix up this code. */
if(!q || !q->head)
return false;
if(sp)
{
strncpy(sp, q->head->value, bufsize - 1);
sp[bufsize - 1] = '\0';
list_ele_t *tmp = q->head;
q->head = q->head->next;
free(tmp);
q->q_size--;
return true;
}
return false;
}
```
### Return number of elements in queue
* 主要目的:回傳 queue 的 size
* 若 queue = NULL ,則回傳 '0'
* 將 element 的個數回傳
```C=1
/*
Return number of elements in queue.
Return 0 if q is NULL or empty
*/
int q_size(queue_t *q)
{
/* You need to write the code for this function */
/* Remember: It should operate in O(1) time */
if(!q)
return 0;
return q->q_size;
}
```
### Reverse elements in queue
* 將 element 反轉
```C=1
/*
Reverse elements in queue
No effect if q is NULL or empty
This function should not allocate or free any list elements
(e.g., by calling q_insert_head, q_insert_tail, or q_remove_head).
It should rearrange the existing ones.
*/
void q_reverse(queue_t *q)
{
/* You need to write the code for this function */
if(!q || !q->head)
return;
list_ele_t *cur, *next, *pre, *shead, *stail;
shead = q->head;
stail = q->tail;
next = q->head->next;
cur = q->head;
while(next)
{
pre = cur;
cur = next;
next = next->next;
cur->next = pre;
}
q->head = stail;
q->tail = shead;
q->tail->next = NULL;
}
```
### 評分結果
```
$ make test
scripts/driver.py
--- Trace Points
+++ TESTING trace trace-01-ops:
# Test of insert_head and remove_head
--- trace-01-ops 6/6
......
+++ TESTING trace trace-14-perf:
# Test performance of size
--- trace-14-perf 7/7
+++ TESTING trace trace-15-perf:
# Test performance of insert_tail, size, and reverse
--- trace-15-perf 7/7
--- TOTAL 100/100
```
## 自動評分系統運作原理
當執行 `$ make test` 時,會執行 `Makefile` 裡的以下這段程式
```
test:qtest scripts/driver.py
scripts/driver.py
```