contributed by < jim12312321
>
kevinshieh0225
list_for_each_safe
。list_del_init
,可讓整體程式碼更安全。container_of
應該不再需要 cppcheck-suppress nullPointer
了q_delete_dup
的規則判定有變,所有在原本佇列中發生重複的節點都要刪除,目前程式碼實作並不符合要求。詳細閱讀 lab0 說明,依據指示著手修改 queue.[ch] 和連帶的檔案,測試後用 Git 管理各項修改。
q_new
: 建立新的「空」佇列q_free
: 釋放佇列所佔用的記憶體q_insert_head
: 在佇列開頭 (head) 插入 (insert) 給定的新節點 (以 LIFO 準則)q_insert_tail
: 在佇列尾端 (tail) 插入 (insert) 給定的新節點 (以 FIFO 準則)q_remove_head
: 在佇列開頭 (head) 移去 (remove) 給定的節點q_release_element
: 釋放特定節點的記憶體空間q_size
: 計算目前佇列的節點總量q_delete_mid
: 移走佇列中間的節點q_delete_dup
: 在已經排序的狀況,移走佇列中具備重複內容的節點q_swap
: 交換佇列中鄰近的節點q_reverse
: 以反向順序重新排列鏈結串列,該函式不該配置或釋放任何鏈結串列節點,換言之,它只能重排已經存在的節點q_sort
: 以遞增順序來排序鏈結串列的節點建立空佇列,若沒有配置指定大小的記憶體,則回傳 NULL。
commit 1be837
「當每次提交一些東西時,Git 會產生一個快照,將所有檔案放置在一個物件記錄著,可以輸入 git log(歷史紀錄) 會看到提交後 40 個字元長 16 進位 的 SHA-1(Secure Hash Algorithm 1)雜湊演算碼,這可以做為提交後的識別碼,使用命令配合 commit 識別碼,一般只需要 6~8 個數字即可辨識。」 – 摘自 git
後來發現在 list.h 中有可以直接初始話佇列的函式 (INIT_LIST_HEAD) ,因此更新。
commit 870612
釋放佇列所佔用的記憶體
由於嘗試許久仍寫不出來,因此以下內容有去參考kevinshieh0225、laneser中q_free的寫法
commit c26a4e
給定要插入的新字串,將新字串加入新節點並中在佇列開頭插入給定的新節點。
若新節點為 NULL 或沒有配置記憶體空間,則回傳 false 。若順利加入佇列開頭則回傳 true 。
commit 870612
commit c26a4e
加入檢查 head 是否為 NULL 的判斷式讓程式更加 robust
commit 58c193
給定要插入的新字串,將新字串加入新節點並中在佇列結尾插入給定的新節點。
若新節點為 NULL 或沒有配置記憶體空間,則回傳 false 。若順利加入佇列結尾則回傳 true 。
commit 870612
跟 q_insert_head 有一樣問題故修正。
commit c26a4e
加入檢查 head 是否為 NULL 的判斷式讓程式更加 robust
commit 58c193
將佇列開頭的節點移除。
commit c26a4e
將 sp 根據 bufsize 的大小規定,在尾端加上 '\0'
commit fd5f90
將佇列結尾的節點移除。
commit c26a4e
將 sp 根據 bufsize 的大小規定,在尾端加上 '\0'
commit fd5f90
回傳佇列長度,若佇列是為 NULL 或空佇列則回傳 0。
以下程式碼源自老師的 lab0 作業要求
commit 392750
移除佇列中間的節點並將其佔用記憶體釋放。
commit 16e285
利用你所不知道的 C 語言: linked list 和非連續記憶體中提到的 fast/slow 指標找中間節點的方法重新改寫。
commit 430645
將佇列中存有重複的節點移除並釋放其記憶體空間。
commit ec6546
將佇列中兩兩相鄰的節點互換。
commit 0508d3
反向順序重新排列鏈結串列,並且不能釋放或配置新的記憶體空間。
commit 3cd169
利用 merge sort q_delete_dup將佇列依照佇列節點中的字串遞增排序。
以下程式優化太糟,無法通過效能測試,已更新較有效率的版本
commit 94ee22
看完 你所不知道的 C 語言: linked list 和非連續記憶體後,利用裡面老師示範的技巧改寫 mergesort
在理解程式碼並實作的過程中,我對於 mergeTwoLists 中的這段程式碼不了解,經過查閱一些資料後,總算理解這樣做的用意,因而紀錄。(以下內容為我個人理解,若有誤,歡迎題點。)
不用在開發紀錄中說客套話。
jservImage Not Showing Possible ReasonsLearn More →
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
一開始我對這段程式碼存有幾點疑惑
uintptr_t 是什麼?
根據 C99 中的規範,提供如 intptr_t,uintptr_t 讓任何可指向 void 的指標可以與之相互轉換且內容不變,因此便可以對指標所指向之地址進行操作 (e.g. void * 無法進行如++或–等數值操作) 。
TODO: 翻閱 C 語言規格書
jservImage Not Showing Possible ReasonsLearn More →
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
參考資料:
intptr_t、uintptr_t数据类型的解析
避免看這種二手資訊,無助於你理解,而且屆時語言標準更動時,你無從掌握
jservImage Not Showing Possible ReasonsLearn More →
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
為什麼這樣可以取代 if-else ?
因為 NULL 經過 uintptr_t 的轉換後就會變成 0 ,因此就可以透過 or 的操作,直接得到不是 NULL 的另一個指標。
為什麼這樣可以增進效能?
跟處理器面對分支 (branch) 時的處理方式有關,若預測錯分支就會造成效能損耗。因此能減少分支的使用就有可能增進效能 (如果每次分支處理都猜對,那有沒有用 if-else 是沒有差別的)
以講義提供的範例程式碼來說,
if
和else
成立的機會約略是各 50%,於是分支預測器就很難總是猜對,你可以翻閱 CS:APP 第 3 章,得知如何分析產生的組合語言輸出,屆時你就會發現,最初的程式碼存在比較和分支跳躍等指令,反過來說,調整後的程式碼只要單純的 bitwise OR 操作,從而省下 CPU 週期。
jservImage Not Showing Possible ReasonsLearn More →
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
參考資料:
代码里写很多if会影响效率吗?
分支預測器 -wiki
去翻閱計算機組織/結構的教科書,裡頭有 branch predictor 的說明,你需要「完整」的認識,而非從隻字片語中找線索,後者的難度比認真讀教科書還高,畢竟你變成「偵探」了
jservImage Not Showing Possible ReasonsLearn More →
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
commit b7014e
commit 39f6ca