contributed by < StevenChou499
>
創造一個空的佇列,先宣告一個指向 list_head
的指標變數 head
,再用 malloc
配置記憶體給 head
,透過 INIT_LIST_HEAD(head)
將 next
以及 prev
均指向自己。若 malloc
失敗則會回傳 NULL
至 head
,所以 malloc
成功會回傳真正的指標,失敗會回傳 NULL
。
修改程式碼:
之後再加上若 head
成功 malloc
再進入初始化,但之後在 make check
時,會一直跳出 there are n blocks still allocated
,目前懷疑是 head
記憶體配置失敗後不會進入初始化,但其實是有成功配置的,因此在配置失敗後再執行 free(head)
釋放記憶體,最後回傳 NULL
。
其中有使用到 list.h
的 INIT_LIST_HEAD()
,其實做方式為下:
INIT_LIST_HEAD()
先定義一指標變數 tmp = l->next
,若 tmp
不等於 l
,則代表還沒回到 head
,因此定義一個指向 element_t
的指標變數 del_el
,由 container_of
找出相對應的 element_t *
,再將 tmp
只向下一個節點,同時釋放 value
以及自己本身的記憶體空間,直到回到 head
為止。最後再 free(tmp)
。
修改程式碼:
之後修改了判斷式,先判斷 l
是否存在,存在後先依序訪問各個節點並將其刪除並釋放記憶體,直到 tmp
剛好與 head
相同時再跳出 while
判斷式,最後將自己也刪除並釋放記憶體。
其中有使用到 list.h
的 container_of()
,其實做方式為下:
container_of()
先透過 malloc
配置空間給指向 element_t
的指標變數 new_el
,若 malloc
成功,則配置記憶體給 new_el
的 value
,利用 strncpy
複製 s
的內容。最後藉由 list_add
加入 head
的下一個節點,回傳 true
,若 malloc
失敗則回傳 false
。
修改程式碼:
先判斷 head
是否存在,若存在則配置一 element_t
的記憶體。若配置成功且 s
並不是 NULL
則配置記憶體空間給新節點的字串,複製字串內容並加入佇列的第一位,再回傳 true
。因為也會遇到配置失敗但 make check
時卻回傳 there are n blocks still allocated
,因次只要配置失敗都會再 free()
相關的指標變數,並回傳 false
。
其中有使用到 list.h
的 list_add()
,其實做方式為下:
list_add()
先透過 malloc
配置空間給指向 element_t
的指標變數 new_el
,若 malloc
成功,則配置記憶體給 new_el
的 value
,利用 strncpy
複製 s
的內容。最後藉由 list_add
加入 head
的上一個節點,回傳 true
,若 malloc
失敗則回傳 false
。完整程式碼如下:
修改程式碼:
先判斷 head
是否存在,若存在則配置一 element_t
的記憶體。若配置成功且 s
並不是 NULL
則配置記憶體空間給新節點的字串,複製字串內容並加入佇列的最後一位,再回傳 true
。因為也會遇到配置失敗但 make check
時卻回傳 there are n blocks still allocated
,因次只要配置失敗都會再 free()
相關的指標變數,並回傳 false
。
其中有使用到 list.h
的 list_add_tail()
,其實做方式為下:
list_add_tail()
先測試 head
是否為 NULL
或是空的,若符合以上規則則直接回傳 NULL
。再利用 container_of
找出 head
的下一個指標。計算出 bufsize
與 strlen(rem_el) + 1
誰比較小,代入 char_len
。若 sp
存在則重新配置記憶體並複製 rem_el->value
的內容,移除該節點並回傳。
修改程式碼:
在進行 make test
時會 trace-07
會有 malloc(): mismatching next->prev_size (unsorted)
的錯誤產生,在將 realloc
去除掉後便沒有錯誤了。
其中有使用到 list.h
的 list_del()
與 list_empty()
,其實做方式為下:
list_del()
list_empty()
先測試 head
是否為 NULL
或是空的,若符合以上規則則直接回傳 NULL
。再利用 container_of
找出 head
的上一個指標。計算出 bufsize
與 strlen(rem_el) + 1
誰比較小,代入 char_len
。若 sp
存在則重新配置記憶體並複製 rem_el->value
的內容,移除該節點並回傳。完整程式碼如下:
修改程式碼:
在進行 make test
時會 trace-07
會有 malloc(): mismatching next->prev_size (unsorted)
的錯誤產生,在將 realloc
去除掉後便沒有錯誤了。
此函式不可更動
先定義一區域變數 i=0
,利用 !list_empty(head)
確認佇列是否為空的,若為空的則定義一個指標變數 tmp
指向 head->next
。若 tmp
不等於 head
則 i++
且 tmp
指向 tmp->next
,最後回傳 i
。
先找出佇列的總節點個數,除以2後建立一指向 struct list head
的指標變數 tmp
,一直跳入下一個節點直到走到中間,利用 list_del
移除該節點後回傳 true
,若 head
不存在或是為空佇列則回傳 false
。
思考避免呼叫 q_size
的方法,降低記憶體存取的數量。
:notes: jserv
好的,學生再想想看,謝謝老師。StevenChou43
修改程式碼:
為了不使用 q_size
計算總節點的數量,我使用一個一次移動一格的指標 slow
與一次移動兩格的指標 fast
。若 fast
已經與 head
重疊或是下一個是 head
,則停止移動並刪除 slow
的節點。最後回傳 true
。
先確認 head
是否存在並且至少存在兩個以上的元素。接著創造三個指向 struct list_head
的指標變數, *tmp1 = head->next
、tmp2 = tmp1->next
和 tmp = NULL
。接著找出 tmp1
與 tmp2
分別代表的字串,若不同則分別至向下一個元素。若遇到字串內容相同,則鄉用 tmp = tmp1
將重複元素中的第一個位置記下來,再將 tmp2
元素刪掉並跳至下一個元素,繼續進行比較,直到不同或是 tmp2
已經與 head
重疊(代表已經完全比完了)則跳出 while
迴圈,並將 tmp1
與 tmp2
的位置各前進一格。此時 tmp
的位置仍是需要被刪除的,藉由 if(tmp != NULL)
可以知道是否有元素重複,若重負責刪除 tmp
的元素。此時仍必須再比較一次看使否 tmp2
與 head
的位置重疊。若重疊則直接跳出迴圈,回傳 true
。
其中有使用到 list.h
的 list_is_singular()
,其實做方法如下:
list_is_singular()
首先檢查 head
是否存在或是只有一個以下的元素,若為上述情況則直接跳出該程式。接著指標 curr
指向 head
的下一個 list_head
,進入迴圈,指標 tmp
指向 curr
的下一個 list_head
。先使用 list_del
除去 curr
,再將 curr
利用 list_add
加入 tmp
的下一個元素位置,接著將 curr
移至下下個位置。繼續上述的動作直到 curr
或是 curr
的下一個元素是 head
。
首先先確認 head
是否存在或是只有一個以下的元素,若為上述情況則直接跳出該程式。接著先定義三個指向 list_head
的指標變數,分別是 prev
指向 head->prev
, curr
指向 head
, next
指向 head->next
。接著是將 curr->next
指向 prev
,curr->prev
指向 next
。並一起跳至下一個元素,直到 curr
再次與 head
重疊,代表已經反轉完畢,結束並返回。
以下將分成 q_sort
原函式, merge_sort
真正的實做部份以及 find_mid
回傳該佇列的中間點。
先判斷傳入的 head
是否存在,若不存在則跳出函式。反之則將 head
拿出該佇列。並將剩下的佇列傳入 merge_sort
。
先確認是否只剩下一個節點,若為真則直接回傳自己。接著利用 find_mid
找出中間的節點,並將兩個佇列分離並各自形成一個環狀雙向佇列,並遞迴呼叫 merge_sort
,直到 left
與 right
都各自只剩下一個節點而已。接著開始進行接合,先將本身的 prev
的 next
指向 NULL
,並定義一個 tmp
指向 NULL
,接著若 left
以及 right
至少存在一個(代表可以進入迴圈),則開始進行比較。若只有 left
存在或是 left
的字串比 right
的字串小,則將 left
的元素置入新的佇列中。若 tmp == NULL
,則代表佇列尚未建立,須將 new_head
指向這新的原點。反之則直接加入即可。最後若 right
以及 left
皆指向 NULL
,則代表已經接合完畢,回傳 new_head
即可。
先利用指標指向 head
與 head->next
,接著各自向前與向後一步,直到兩者重疊或是相鄰,再回傳該指標即可。
先打開 qtest.c
並依照 lab-0 實做 do_hello
後,可以成功出現 Hello World 的訊息。
先打開 qtest.c
檔後,進入 main()
主程式找到 run_console(infile_name)
後,會發現 run_console()
這函式是位於 console.c
的檔案中。打開 console.c
後,找到 run_console
的函式,發現它傳入的引數為 infile_name
,我認為應該是傳入的檔案名稱。進入 run_console
後,會檢查是否有 has_infile
,若有則會進入 else
的區域,且會持續執行 cmd_select()
直到 cmd_done()
為止。
以下為 run_console
的程式碼:
接著也可以在 console.c
找到 cmd_select()
,在該函式中找到 /* Commandline input available */
的註解。下面還有 if(has_infile)
,進入之後還有 cmdline = reeadline()
和 interpret_cmd(cmdline)
,我認為這應該是確認是否有輸入指令,若指令存在則開始進行翻譯的動作。
以下為 cmd_select()
部份的程式碼:
找到 interpret_cmd()
後,可以看到其中有 parse_args(cmdline, &argc);
與 bool ok = interpret_cmda(argc, argv);
,我認為這兩個函式應該是先解析出共有幾個指令並且傳送出指向指令的位址。
以下為 interpret_cmd()
的程式瑪:
接著找到 interpret_cmda()
的函式,其中會有 cmd_ptr
指向所有的指令,經過 while
迴圈一直尋找相同指令後,最後再進入該 next_cmd
的 operation
。執行相對應的指令動作,若沒有找到該指令則會印出 report(1, "Unknown command '%s'", argv[0]);
,印出剛剛輸入的指令名稱。
以下為 interpret_cmda()
的程式碼:
當輸入不存在的指令時(輸入 wrong 為指令):
在大致了解程式運作流程後,為了增加 shuffle
的指令,我們必須先在 console_init()
中加入該程式碼:
這樣在輸入 shuffle
該指令時便系統會自動去尋找 do_shuffle()
的函式,因此我們還需要在 qtest.c
中加入 do_shuffle()
的函式。
以下為 do_shuffle()
的完整程式碼:
程式碼中的前兩個判斷式是我複製檔案中其他函式的。第一個是確定傳入的引述個數只有一個,第二個是確認該佇列不是指向 NULL
。接著開始進行 q_shuffle()
的函式, shuffle()
完成後再使用 show_queue()
依序展現每個節點。
以下為 q_shuffle()
的完整程式碼:
先確認傳入的佇列不是空節點或是只有一個以下的節點,接著找出佇列的節點數量。並依照 Fisher-Yates Shuffle 的方法先使用一個指標分開已經洗完牌與尚未洗牌的節點。接著隨機尋找要交換的節點,再使用 Linux 的內建的 API 實現交換的過程,最後回傳 true
。
成功!!
在終端機輸入 make valgrind
後,系統跳出了非常多的錯誤,且幾乎都是 still reachable
的情況。
先試著 valgrind ./qtest
,並沒有發現任何錯誤。
接著試著測試每個測試檔是否有記憶體的問題:
輸入 valgrind ./qtest -f ./traces/trace-01-ops.cmd
後,會出現三個問題,皆為 still reachable
的情況,且皆來自 linenoiseHistoryAdd
的函式。
linenoiseHistoryAdd
的原始程式碼:可以看到在 1224 行的 strdup
與 1236 行的 malloc
皆有 still reachable
的情況。我想應該是因為在配置繼體後使用完畢沒有釋放造成的,因此我在程式 history_len++
後方加上釋放記憶體的動作。
更改後的程式碼:
執行結果:
lib/list_sort.c
原始程式碼之效能:
新建一個 trace-time.cmd
檔,隨機插入 300000 個元素,以測試 sort
的效能。
以自己撰寫的 q_sort.c
實測排序的效能,在終端機輸入 make check
可以得出排序的時間約為 0.263 秒。