contributed by < GOGOGOGOGOGOG
>
前置作業
-關於git以及關於git以及clang的設定與安裝
-關於Linklist
測試環境
作業要求
作業說明
自動評分系統
sudo apt install clang-format
安裝之後queue.c檔進行format,否則無法編譯,利用 clang-format -i queue.c
分別將queue.c檔以及queue.h檔進行format。mkdir <folder>
開啟一個新的資料夾後,cd <folder>
進入該資料夾並寫入指令git clone <https://github.com/GOGOGOGOGOGOG/lab0-c.git>
把 repositories 拉到你的系統中,https://github.com/GOGOGOGOGOGOG/lab0-c.git 這段網址是你的repository。git init
必須先進行,修改完之後,在儲存該程式碼的資料夾中打入git add .
將全部檔案都加入到要上傳的地方後,接著輸入git commit -m "what you have done"
。git push --set-upstream origin master
並打入git push
輸入帳號名稱和密碼後即可上傳,在上傳過程中要特別注意branch名稱和上傳的位置,否則會有下列狀況:error: src refspec homework does not match any.
error: failed to push some refs to 'https://github.com/GOGOGOGOGOGOG/lab0-c.git'
鍵入完成會出現以下畫面
安裝若有問題可以參考以下網址:https://stackoverflow.com/questions/4181861/src-refspec-master-does-not-match-any-when-pushing-commits-in-git
git add .
後打入git commit -m "Modefiedchange queue.c"
要打入修改了哪些東西和第一個字要大寫,否則會出現系統不斷要求你重新commit的情況。如下:輸入完正確指令後會出現:
Commitmodifiedq_free queue.c
1 file changed, 5 insertions(+), 1 deletion(-)
也可以用git log
和 git status
來確認上傳狀況。
在大學時期由於修課的關係,並不常接觸c語言,對於結構也不是很了解也不知道linklist,於是曾經看過以下的影片和書籍
亦即我們可以透過 struct來組合兩個完全不同的型態,而透過利用malloc函式來進行動態記憶體的配置,malloc函式會回傳所配置的記憶體位址,所以必須以一個指標變數來接收它。因為指標變數會有它所指向的型態,因此我們把malloc函式所傳回的位址先進行型態轉換,再把它設給指標變數存放。如果配置失敗,則malloc函式傳回NULL。而本次的作業的header檔裡可以發現,本次有兩個結構,其結構變數分別是:list_ele_t和queue_t。我的解讀和編排是,本次作業分成大結構與小結構,小結構(list_ele_t)用來表示輸入的字串和指向下一個的位址,而大結構(queue_t)則用來表示輸入的字串所形成的小結構之排頭和排尾以及總字串大小。
詳看:http://www.cs.cmu.edu/~213/labs/cprogramminglab.pdf
裡面提到我們可以自由添加變數。
首先針對 queue.h
一開始我們必須先設定我們的全域變數:
由註解我們可以得知需要一個指標陣列來儲存字串和一個指標來儲存下一個字串的位址。
而在大結構中:
我們由註解可以得知,需要一指標tail和表示字串大小size。
其中return的語法為:若q不為NULL則查看qsize0,否則直接判定為0。
其方法為用while迴圈查看所有的字串和節點並將其刪除。但似乎有些東西尚未清無乾淨(bcnt)?,使用gdb下去看q_free函式。
(10/6更新)後來發現free本身並沒有問題,問題是出現在sizeof和strlen的用法,前者會造成qtest的do_insert_head中q-_insert_head會回傳false
於是我做了一個實驗,在insert_tail打入strlen而在insert_head裡保持原樣,結果顯示前者的回傳值為true,後者為false
(10/10更新)
回傳的為一個指標s和一個結構指標,而所接受的字串也為一個指標陣列,為了將接受的字串傳入newh的value中我使用了strcpy函式,但在這之前,其實在一開始我是使用strdup的,但後來出現了一些問題,這裡我會在q_insert_tail做解釋以及參考了[pjchiou
]同學的為何不能使用strdup。
這裡一開始我的想法同樣和insert head一樣給予一個指標變數暫存原先的tail指標,但後來經過詢問同學後,發現其實這麼做只會浪費記憶體空間和降低效率而已。然而後來我原本是使用strdup(如下面註解)但總是會有segamation fault的情況發生,為了觀察這種狀況,我參考網路上關於linux系統除錯(http://www.cnblogs.com/panfeng412/archive/2011/11/06/2237857.html)
在參考下,我使用了gdb以及core文件,透過man 7 signal,我們發現其默認的handler會在出現Segmentation Fault時打印出core文件,以此來觀察其位址哪裡出錯。
首先我們給core文件一個記憶體 ulimit -c 1024
並且告至他在出錯時產生文件./segfault3
其執行畫面如下圖:
在圖形中core文件告訴我insert tail和free以及reverse等地方出現了corrupted block,經過參考了[pjchiou
]同學的共筆後,查了一下c語言規格書,發現關於strdup的定義
The strdup() function returns a pointer to a new string which is a duplicate of the string s. Memory for the new string is obtained with malloc(3), and can be freed with free(3).
harness.c
會被看成一個自定義的函式。也就是說使用 strdup
,就會變成在用 malloc
與 test_free
便會自動偵錯。另外參考網路上的一些見解,strdup本身是一個不是一個標準的c函式,所以linux系統會自動偵錯。(待確認)()裡的編號是執行的順序
A B C表示位址
head: A–->B(2)–->C(5)–->NULL–->C
tail: A
last: NULL–->A(1)–->B(4)–->C(7)
current: B–->C(3)–->NULL
方法是先將q->tail和q->head都指向q->head,透過while迴圈一個結構一個結構改變指向的方向,最後再將last的位址指派給q->head,因為last經過while迴圈後最後的儲存位址為原本的最後一個結構,最後將q->head指派成最後一個結構,即完成reverse。
這裡的想法是:先處理關於刪減掉頭結構後,size會減少的情況,首先定義size的大小,將value指派給size。接下來考慮到除去頭後,size減少的情況。
接著開始清除head資料,首先將q->head儲存到一個指標變數中,接著把下一個結構的位址指派給q->head,定義為新的q->head,然後釋放移除的記憶體。
現況:
改動後為100分
剩下7分實在不確定是哪裡出了問題,透過gdb和core tmp只可以看出是在執行qtest的時候,free函式出現了memory leak,原因待查詢,避免時間來不及先研究自動評分系統。(10/4)
首先來看qtest.c裡面的程式碼,可以發現do_new
的布林運算對應到的是執行q_new
函式,而do_free
對應的是執行q_free函式等等…
而在static void console_init()
是對於各個cmd進行定義和宣告,也增加了錯誤提示訊息等等… 而其中最讓我好奇的便是 在do_free進行宣告的bcnt和allocation_check(),似乎被扣7分的原因在於我的bcnt為清除乾淨,向學長討教後,發現所謂的bcnt指的是block count,也代表有暫存的記憶體並沒有清除乾淨,於是我決定再用gdb進行觀測,順便了解這次的評分機制。
首先我們將qtst進行gdb檢測並且在do_free進行break,如圖:
可以看到後來的數據逐一印出後,基本上都沒什麼問題,直到進入 bcnt的那一行程式碼,如圖:
重新在進行測試一次:
可以看到即使經過q_free後,我們的記憶體依舊沒有清除乾淨,這讓我懷疑是不是自己的q_free的程式邏輯出了一點問題,決定之後再來好好檢查看看。除此之外可以看到有一個trigger_exception( "Time limit exceeded. Either you are in an infinite loop, or your " "code is too inefficient");
這是一個測試程式執行時間的函式,在這個函式中只要接收到signum
的訊號時就會執行底下的handler
函式。在qtest.c裡面可以看到:
將原程式碼改為:
會出現core dump的錯誤,不太確定是因為strdup不是c語言標準函式而在harness.c裡被看成一個自定義的函式而出錯?
harness.c:
為什麼在 do_remove_head
中會使用到memset? 其使用的目的是什麼?
又為什麼 do_remove_head_quiet
並沒有使用到呢?
使用gdb來看memset:
13 = 0x60da31 'X' <repeats 200 times>…看不懂是什麼意思?
c語言規格書中的 memset :
意思是:
s : 填充的內存塊的指標
c : 要設置的值。作為一個int值傳遞,但使用這個值的無符號字符型轉換函數填充的內存塊。
n : 要設置的值的字節數
3/12更新:
最近更新來重新檢視之前上學期寫的一些程式碼,進行程式簡化和自己回答一些自己上述的問題。
如果將程式碼改為:
會發現strdup會顯示error,但參照規格書後其程式碼是沒有問題的原因是出在function hooking的身上,因為我們在定義free和malloc時,會被定義為test_free和test_malloc,也就是說其外層會包一層macro來保護程式碼不會crash(例如:雙malloc),而什麼是function hooking呢?我參考了以下的文章:
Function Hooking ,裡面其中提到:Function call hooking refers to a range of techniques used to intercept calls to pre-existing functions and wrap around them to modify the function’s behavior at runtime
所以如果我們想要strdup能夠正常使用的話就必須自己寫strdup的外層macro將其寫在harness.c裡面,例如test_free這樣:
持續更新中…