owned this note
owned this note
Published
Linked with GitHub
# 2018q3 Homework2 (lab0)
contributed by <`plusline`>
>* 請補上實驗環境
>* 中英文字間請以空白隔開
>
>[name=課程助教][color=red]
>* fixed
>* 助教這次作業我已經完成,但還是收到進度落後的通知
>* 請問是哪部分寫得不完整嗎?
>
>[name=plusline][color=blue]
>>作業要求還有提到需「解釋自動評分系統運作的原理」、「提及 qtest 的行為和裡頭的技巧」這兩部份,因為沒看到你的紀錄,而且多天未更新,所以才寄信通知。
>>應該是你漏掉這部份了XD
>>還有時間,GOGO!
>>[name=課程助教][color=red]
>>
> 我漏掉那部分的要求,所以開始在看下一周的作業,難怪都沒有共筆可以參考,我會繼續補齊的
>
> [name=plusline][color=blue]
### 測試環境
```click
plusline@plusline-System-Product-Name:~$ cat /etc/os-release
NAME="Ubuntu"
VERSION="18.04.1 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.1 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic
plusline@plusline-System-Product-Name:~$ cat /proc/version
Linux version 4.15.0-34-generic (buildd@lgw01-amd64-047) (gcc version 7.3.0 (Ubuntu 7.3.0-16ubuntu3)) #37-Ubuntu SMP Mon Aug 27 15:21:48 UTC 2018
```
### git
- 先到你要 fork 的專案按下 fork 按鈕,它會產生一個副本到你的 github 帳號。
- 回到自己 github 首頁,可以找到剛剛 fork 過來的 repositories 。
- `git clone https://github.com/plusline/lab0-c.git` 把 repositories 拉到你的linux,這段網址是你的 repositories 的位置。
- `git checkout -b slow-blink` slow-blink 是你要創建的分枝。
- 在你的電腦做完所有修改後
- `git commit -a -m 'What you have done'` 寫一點註解
- `git push origin slow-blink` 提交
[git](https://git-scm.com/book/zh-tw/v2/GitHub-%E5%8F%83%E8%88%87%E4%B8%80%E5%80%8B%E5%B0%88%E6%A1%88)
### 自動評分系統
注意:需要安裝 python 而不是 python3 才能執行 `driver.py`。我一直以為是bug,直到打開 `driver.py` 發現print的格式和我印象中不一樣,才知道原來 python 有兩個分枝。
`clang-format -i queue.c` 用來排版
`make`用來編譯,修改完 queue.h 和 queue.c 後執行。
`make test` 用來呼叫 `driver.py` 會幫你的程式評分。
```clike
--- TOTAL 0/100
```
什麼都沒做,所以拿了0分。
## Implementation
### free
```click
void q_free(queue_t *q)
{
/* How about freeing the list elements and the strings? */
/* Free queue structure */
if (q == NULL)
return;
list_ele_t *temp, *current;
current = q->head;
while (current != NULL) {
temp = current;
current = current->next;
// free(temp->value);
free(temp);
}
if (q != NULL)
free(q);
}
```
用 for 迴圈遍歷,並將所有的節點和字串刪掉,最後再刪掉 queue 。
一直搞不懂為什麼不需要刪掉字串的空間?
---
### insert head
```click
bool q_insert_head(queue_t *q, char *s)
{
if (q == NULL)
return false;
list_ele_t *newh;
/* What should you do if the q is NULL? */
newh = malloc(sizeof(list_ele_t));
/* Don't forget to allocate space for the string and copy it */
/* What if either call to malloc returns NULL? */
if (newh == NULL)
return false;
newh->value = strdup(s);
newh->next = q->head;
q->head = newh;
if (q->size == 0)
q->tail = newh;
q->size += 1;
return true;
}
```
我總覺得直接給 *value 一個給定的大小不太好,但不知道怎做,先將就一下。後來已經修正成用 strdup ,就不用擔心空間大小的問題。
---
commit 時出現錯誤,但編譯沒報錯:已修正,似乎是 q_insert_head 隨意離開函式引起的。
```click
[queue.c:71]: (error) Memory leak: newh
Fail to pass static analysis.
```
---
```click
plusline@plusline-System-Product-Name:~/lab0-c$ make
gcc -O0 -g -Wall -Werror -o qtest qtest.c report.c console.c harness.c queue.o
queue.o: file not recognized: File format not recognized
collect2: error: ld returned 1 exit status
Makefile:15: recipe for target 'qtest' failed
make: *** [qtest] Error 1
```
後來發現是不小心輸入 `clang-format -i queue.o` ,在輸出 queue.o 的檔案( queue.c queue.h )還沒更新前,使用 makefile 並不會執行 `gcc -O0 -g -Wall -Werror -c queue.c ` ,只需要刪掉 queue.o 就可以了。
---
### remove head
```click
bool q_remove_head(queue_t *q, char *sp, size_t bufsize)
{
/* You need to fix up this code. */
if (q == NULL || q->size == 0)
return false;
list_ele_t *dummy;
dummy = q->head;
if (sp == NULL)
return false;
strncpy(sp, q->head->value, bufsize - 1);
sp[bufsize - 1] = '\0';
q->head = q->head->next;
free(dummy);
q->size -= 1;
return true;
}
```
第六個測資是檢測 truncated strings ,測資會故意截斷字串,導致沒有 null terminator ,需要自己將後面一個字元改成`'\0'`。
---
### 大功告成
其他的部份照著註解做,都沒有遇到太大的問題。
```
--- TOTAL 100/100
```
### 自動評分系統運作的原理
依照作業手冊,我們知道要評分的時候要在 lab0-c 下執行 make test,我從這裡開始 trace 。打開 Makefile ,找到 test: ,可以知道它呼叫 scripts/driver.py 。
接下來就是 trace driver.py ,從 `if __name__ == "__main__":` 進入,呼叫 run ,在 run 裡會設定參數,並創建 class Tracer ,並執行 class 中 run 的方法,在這裡它會呼叫放在 traces 資料夾底下所有要測試的題目,其中 subprocess.call 就是用來呼叫這些題目的方式,舉個例子:在15個測資,它需要傳入的參數就是 ```['./qtest', '-v', '1', '-f', './traces/trace-15-perf.cmd']```這等於直接在命令列打上`./qtest -v 1 -f ./traces/trace-15-perf.com`這個指令執行完,如果這部份的測資答對就會回傳0,答錯就會回傳1,一此判斷加分與否,將所有題目測試完就完成自動評分了。其中 qtest.c 是評分系統判斷作答正確與否的核心,
-----
### qtest 的行為和裡頭的技巧
當不確定輸入的參數數量的時候,可以使用下列這種語法。[va_start](https://linux.die.net/man/3/va_start)
```c
void
foo(char *fmt, ...)
{
va_list ap;
int d;
char c, *s;
va_start(ap, fmt);
while (*fmt)
switch (*fmt++) {
case 's': /* string */
s = va_arg(ap, char *);
printf("string %s\n", s);
break;
case 'd': /* int */
d = va_arg(ap, int);
printf("int %d\n", d);
break;
case 'c': /* char */
/* need a cast here since va_arg only
takes fully promoted types */
c = (char) va_arg(ap, int);
printf("char %c\n", c);
break;
}
va_end(ap);
}
```
要操作這個語法需要宣告一個 va_list 的物件,然後用 va_start 初始化,之間用 va_arg 取值,最後在用 va_list 解構。
練習
```c
void too(char *fmt, ...){
va_list ap;
va_start(ap,fmt);
char *s;
int count=*fmt-48;
printf("%d\n",count);
s=va_arg(ap,char *);
while(count--){
printf("%s\n",s);
s=va_arg(ap,char *);
}
}
int main(){
too("4","lion","tigre","mouton","oiseau");
return 0;
}
```
```
4
lion
tigre
mouton
oiseau
```
要小心用 va_arg 不能取超過參數數量,不然會發生錯誤。