contributed by < siahuat0727
>
請補上實驗環境
課程助教
利用已寫好的 q_remove_head
完成 q_free
的要求,減少不必要的錯誤。
第 7 行處一開始爲 while (q->head)
,在實作 size
之後改爲現在的樣子,提高可讀性。
過程中第 26 行 newh->next = NULL
忘了賦值間接造成測 q_reverse
時(Test 4)出錯。(無法判斷結束)
第 10 行存在原因
strncpy 的 man page description 中:
Warning: If there is no null byte among the first n bytes of src, the string placed in dest will not be null-terminated.
發現要 free(tmp->value)
時會出錯,閱讀其他人的開發記錄發現大家都遇到同樣的問題,其中 pjchiou
同學有發現 harness.h
中有一段 #define free test_free
,具體原因需要再研究。
是 strdup 惹的禍,因為 harness.h 有段 macro 會把 free 換成一個自己開發的函式,導致你用不到真正的 free ,變成在用 malloc 與 test_free ,因此直接用 malloc 讓 preprocessor 把它也轉成 test_malloc 就好了。
pjchiou2018 Sep 27 Thu 06:08感謝
pjchiou
同學的留言,已將strdup
改爲malloc
並把創建 list element 獨立出來避免 WET (write everything twice)。git commit
siahuat0727Thu, Sep 27, 2018 12:47 PM
利用 q->head
和 q->tail
指向已 reverse 的 subqueue,list_ele_t *e
指向待 reverse 的 subqueue 的起始 element。
若使用 strdup
, 系統沒辦法偵測是否發生 memory leak 及 memory allocation 失敗的情況,詳見了解自動評分系統。
harness.h 中有一段:
也就是我們在 queue.c 裡呼叫 malloc
和 free
都會被取代爲 test_malloc
和 test_free
,後者會建立一個 doubly-linked list 來保存我們每次 malloc
和 free
的資訊,其資料結構如下:
這裡 payload
用到 Arrays of Length Zero 的技巧,其中 payload
只在其他處出現過一次:
不用 payload
也可以達到同樣的效果,其在此處等價如下,只是可讀性較差。
爲什麼不寫 (void *)new_block + sizeof(block_ele_t)
?
Arithmetic on a void* is illegal in C, it's GCC extension.
強調在此處是因爲這跟 alignment 有密切關係,假設今天 block_ele_t 改爲這樣:
那麼在有 alignment 且不改動其他程式碼的情況下就只有第二種方式可以執行成功了,原因是 new_block
在賦值時是這樣的:
這裡的設計是把所需的 size
接在 sizeof(block_ele_t)
後面使用,而 block_ele_t
在有 alignment 時會在 structure 最後進行 padding,但 payload
是緊接在 char dummy
之後的。
Arrays of Length Zero 中有一句話:
The offset of a zero-length array member from the beginning of the enclosing structure is the same as the offset of an array with one or more elements of the same type. The alignment of a zero-length array is the same as the alignment of its elements.
看了好久看不懂,還找 aben20807
同學討論,最後終於搞懂了。
簡單來說,要計算 array of length zero 在 structure 中的位移,就是把它改成 array with one element 後的計算方法一樣:
Output:
Arrays with length zero 通常是作爲整個 structure 的最後一個 member,這裡只是方便理解內容。
再看這段程式碼:
最後的 sizeof(size_t)
用來存某些特定的值(作者稱之爲 Special values),被安排在 size
的後面(整個可用空間的最後面,footer),也就是過程中哪怕只有 1 byte 的 buffer overflow 都可以在 test_free
時被測出來。
而 structure member size_t magic_header
用法也相同,可以檢查是否爲 unallocated memory 或被不合法的修改。
另外,一開始也好奇爲什麼 test_malloc
裡呼叫的 malloc
不會轉成 test_malloc
?
原因如下, harness.c:
先 #define INTERNAL
,就不會 include 將 malloc
和 free
轉換的 macro 了。
點此往上查看 harness.h 大致內容
指令的第一個參數(如 ih some_string
的 ih
)對應個別 function,並用如下資料結構以 linked list 串接:
每個指令會先經過 char **parse_args(char *line, int *argcp)
轉爲 argc
和 argv
的形式,統一對應 function 的 prototype:
在 if-block 裡面觸發 signal 時,會由 signal handler 執行對應的指令後跳出 if-block。
exception_setup
:通過 sigsetjmp
設定目標位置。若觸發 signal ,最後會透過 siglongjmp
跳回目標位置,又因爲 exception_setup
那時將 return false
而跳出 if-block。
exception_cancel
:作用是限制異常處理的範圍,在 exception_setup
與 exception_cancel
之外的地方若觸發 signal 將導致程式強制結束。
vprintf
report.c 中用了很多這技巧,如:
void report(int level, char *fmt, ...)
void report_noreturn(int level, char *fmt, ...)
int vprintf(const char *format, va_list ap);
equivalent to the functions printf() except that it is called with a va_list instead of a variable number of arguments. – 截取自 man page (小改動)
兩者結合,以下 my_printf
與 printf
等價: