contributed by < ab37695543xs
>
CMU Introduction to Computer Systems (ICS) 準備了 C Programming Lab 作為檢閱學生 C 語言程式設計的認知:
實驗目標為實作 queue
為了滿足 O(1) 的時間要求下,實作出 q_insert_tail()
與q_size()
,便在資料結構上新增了 tail
與 size
來紀錄
成功分配記憶體的話,就初始化 q
,反之 q
會回傳 NULL
由於透過額外 field 來記錄 queue 的大小,且在 q_new()
已初始化,這邊直接回傳即可
犯錯的地方:
由於初始化是在 q_new() 時進行,需要考慮到未初始化的情況,即 q 為 NULL
q
與記憶體分配的 malloc()
與 strdup()
為 NULL
的可能由於透過額外 field 記錄 tail 位置,所以省去遍尋的時間,剩下邏輯同 q_insert_head()
strdup()
分配記憶體,所以要手動 free犯錯的地方:
- 只要有參照指標 q 的地方,都應該檢查 q 是否為 NULL
- 雖然 free(NULL) 是可行的,但在 while 裡先使用到
q->head
,所以會先失敗
sp
,同時有限制複製的字元數不超過 bufsize
bufsize
的字串不會複製到 '\0'
,這邊是先將 sp
統一設為 '\0'
,再將 bufsize - 1
以內的字元複製過去犯錯的地方:
- 一開始只考慮到檢查 head 而已,忽略了 q 為 NULL 的情況
- 字串複製時忽略了 sp 為 NULL 時,會造成無法寫入的情形
反轉的部分一開始寫的是後面的版本 (由尾到頭依序),後來改良的是以下版本 (由頭到尾依序)
prev
、head
和 next
來完成交換。newt
,需要考慮最後完成的 tail 要接 NULLtmp
和 tail
來完成交換,缺點是每次都需要從頭找到 tail 前一個newh
make test
會等同執行 makefile 裡以下指令driver.py
後,首先會檢查有沒有後續的引數-p
可以決定待測試的程式,-t
挑選測資題目,-v
決定細節顯示 (0~3 越高越細),或是 --valgrind
以 valgrind 開啟程式有個隱藏引數是
-A
,開啟 autograde (以 JSON 字串印出),但因為過程中就會自動計算分數,所以預設不會用到
run
後,主要處理上述引數run
valgrind ./qtest
./qtest
事實上下半部的 command 可以不用再次賦值,預設就是
./qtest
,不過這樣可以增加可讀性
tid
未指定測資的情況下,會依序跑完所有測資verbLevel
幫助了解subprocess.call
來呼叫 qtest這邊事實上也可以將
fname
的部份全部預設寫在 dict 裡面,拆開來只是為了精簡過程中顯示的文字
qtest.c
裡,使用的是 getopt() h
v:
f:
l:
即是代表要處理的引數-h
、-v
這樣的形式才會擷取:
表示後面需要有對應的資訊,如 -f test.txt
,該資訊會存在 optarg
這個固定的變數-1
、?
和 :
driver.py
裡,使用的是 getopt.getopt() C-style parser,而 python 也有提供比較強大的模組是 argparsevalgrind
是透過 list 來儲存,在 C 則需要呼叫 getopt_long()
,以特別的 struct 來儲存optlist
和 args
可以更改變數名稱,前者以 list 儲存所有擷取到的引數,後者是儲存擷取後剩餘的引數(option, value)
形式儲存,value
只有在指定 :
時才會一併把資訊放入-f
和 -v
對應到上述自動評分提及的腳本與細節顯示,-l
則是指定紀錄檔存放的位置SIGSEGV
處理記憶體錯誤,SIGALRM
則處理定時器harness.c
的 trigger_exception()
,注意 handler 會有固定一個參數儲存信號queue.c
對應的函式裡,扣除引數的檢查後,都會先檢查 q 是否為 NULLharness.c
的 error_check()
,因為尚未測試 queue,這邊忽略回傳值,目的只是重設 error_occurred
do_new()
為例,完成上述步驟後會先設置一個存檔點,true
表示允許開啟定時器,do_free()
會開啟 harness.c
的 cautious_mode
,確保任何要 free 的區塊目前都 allocated,但似乎不會用到 (?)影響到
find_header()
,只有test_free()
用到,但沒有呼叫到test_free()
的機會
do_reverse()
也會開關 noallocate_mode
,開啟的情況下會禁止 test_malloc()
和 test_free()
一樣沒有呼叫到兩個函式的機會
env
會儲存當下的 stack 資訊,宣告雖然可以用 jmp_buf
,但可以改成 sigjmp_buf
比較有一致性volatile
表示該變數能夠在 set 和 jump 之間存取,且不會套用編譯器的最佳化,避免影響變數的更新sig_atomic_t
表示變數的更動需要在一個指令內完成,所以兩個關鍵字常一併出現time_limited
表示是否有開啟定時器time_limit
表示定時器的間隔,預設一秒savesigs
的位置傳入 1,根據 signal,表示能夠處理 SIGHUP 程序中斷的情況exception_setup(true)
sigsetjmp()
成功會是回傳 0,跳到 elseSIGALRM
就回傳 true
SIGSEGV
trigger_exception()
,記錄對應的訊息siglongjmp()
會回到上一個存檔點 sigsetjmp()
qtest.c
測試的 if 區塊不再執行SIGSEGV
,最終都會呼叫 exception_cancel()
,差別在發生例外的情況下會多做一次而已harness.c
的 error_check()
error_occurred = false
trigger_exception()
,就會維持 error_occurred = true
,使得最終回傳值為 false,表示失敗