ChihAnLin0607
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # 2019q1 Homework1 (list) contributed by < `ChihAnLin0607` > ## 1. 為何 Linux 採用 macro 來實作 linked list?一般的 function call 有何成本? 每一個 function call 之前都需要先將當下的狀態(暫存器) push 進 stack 中保存,然後才能跳至函式內部執行。執行完後也需要再將 stack 中的狀態 pop 出來還原。 而 macro 在編譯階段,就將呼叫 macro 的位置用定義好的敘述展開,所以不需要先 push 狀態,結束後再 pop 狀態。 所以總結來說, macro 作法是用空間換取時間,雖然少了狀態保存與恢復的動作與時間,但是會增加程式碼的長度; function call 則是用時間換取空間,雖然每一次的呼叫都需要狀態切換的時間成本,但是不論呼叫多少次,都是執行記憶體空間內的同一段代碼。 為了驗證以上論點,我做了以下兩支程式:macro.c和func_call.c,他們行為很簡單,就是呼叫 foo() 110000 次,然後 foo() 會將一個全域變數 i 的值增加 1: ```clike //macro.c #define foo() i++ int i = 0; int main() { // 一共 22 * 5000 個 foo() foo();foo():foo();foo():foo();foo(); foo();foo():foo();foo():foo();foo(); foo();foo():foo();foo():foo();foo(); . . . printf("Elapsed Time: %f sec.\n", (double)clock()/CLOCKS_PER_SEC); return 0; } ``` func_call.c 和 macro.c 很像只是將 foo 改成函式: ```c //func_call.c int i = 0; void foo() { i++; } int main() { // 一共 22 * 5000 個 foo() foo();foo():foo();foo():foo();foo(); foo();foo():foo();foo():foo();foo(); foo();foo():foo();foo():foo();foo(); . . . printf("Elapsed Time: %f sec.\n", (double)clock()/CLOCKS_PER_SEC); return 0; } ``` clock() 會回傳程序執行開始到被呼叫時,CPU 花在該程序的 clock 數,除上定義於 time.h 中的 CLOCKS_PER_SEC 就可以得到呼叫 clock 之前花了多少時間。 分別將兩者編譯成 macro 和 func_call: ```shell $ gcc macro.c -o macro $ gcc func_call -o func_call ``` 若上方推論正確,應該可以得到以下結果: * macro會比func_call佔用更多空間 * macro的執行速度會比func_call快 對兩者分別測試20次的結果如下(單位為秒): macro: 0.009456、0.009505、0.009262、0.009319、0.009517、0.009465、0.009251、0.009339、0.008269、0.009556、0.010748、0.009442、0.009563、0.009321、0.007704、0.009247、0.009625、0.009290、0.007781、0.004047 func_call: 0.012496、0.018345、0.012943、0.013356、0.012168、0.013422、0.014048、0.012483、0.011679、0.015657、0.015038、0.015581、0.015448、0.014399、0.013235、0.015658、0.007988、0.012845、0.014701、0.016542 雖然其實應該要用統計方法(虛無假設)決定兩者所花時間一不一樣,但我決定先寫其他題目,這邊先用肉眼判斷<u> macro 所花時間明顯比 func_call 少</u>。 至於原因,可以利用objdump看出其中端倪: ```shell $objdump -D macro > macro.lst $objdump -D func_call > func_call.lst ``` 以下是 macro.lst 的部份內容: ``` 00000000004004d6 <main>: 4004d6: 55 push %rbp 4004d7: 48 89 e5 mov %rsp,%rbp 4004da: 8b 05 54 cb 1b 01 mov 0x11bcb54(%rip),%eax # 15bd034 <i> 4004e0: 83 c0 01 add $0x1,%eax 4004e3: 89 05 4b cb 1b 01 mov %eax,0x11bcb4b(%rip) # 15bd034 <i> 4004e9: 8b 05 45 cb 1b 01 mov 0x11bcb45(%rip),%eax # 15bd034 <i> 4004ef: 83 c0 01 add $0x1,%eax 4004f2: 89 05 3c cb 1b 01 mov %eax,0x11bcb3c(%rip) # 15bd034 <i> 4004f8: 8b 05 36 cb 1b 01 mov 0x11bcb36(%rip),%eax # 15bd034 <i> 4004fe: 83 c0 01 add $0x1,%eax 400501: 89 05 2d cb 1b 01 mov %eax,0x11bcb2d(%rip) # 15bd034 <i> ``` func_call.lst 部份內容如下: ``` 00000000004004d6 <foo>: 4004d6: 55 push %rbp 4004d7: 48 89 e5 mov %rsp,%rbp 4004da: 8b 05 54 eb c7 00 mov 0xc7eb54(%rip),%eax # 107f034 <i> 4004e0: 83 c0 01 add $0x1,%eax 4004e3: 89 05 4b eb c7 00 mov %eax,0xc7eb4b(%rip) # 107f034 <i> 4004e9: 90 nop 4004ea: 5d pop %rbp 4004eb: c3 retq 00000000004004ec <main>: 4004ec: 55 push %rbp 4004ed: 48 89 e5 mov %rsp,%rbp 4004f0: b8 00 00 00 00 mov $0x0,%eax 4004f5: e8 dc ff ff ff callq 4004d6 <foo> 4004fa: b8 00 00 00 00 mov $0x0,%eax 4004ff: e8 d2 ff ff ff callq 4004d6 <foo> 400504: b8 00 00 00 00 mov $0x0,%eax 400509: e8 c8 ff ff ff callq 4004d6 <foo> ``` 由上面兩個 objdump 出的內容可以看出,macro 的 main 函式中,會將 foo() 換成以下三行組合語言代碼: ``` mov 0x11bcb54(%rip),%eax # 15bd034 <i> add $0x1,%eax mov %eax,0x11bcb4b(%rip) # 15bd034 <i> ``` 簡單來說就是把 i 的值放進 eax 中,然後把 eax 的值增加一,再把新的 eax 值存回 i 中來完成 i++。 而func_call則是把foo()翻成: ``` mov $0x0,%eax callq 4004d6 <foo> ``` 直接跳去 foo 函式所在的記憶體位置,而foo函式內容除了有剛剛 macro.lst 中看到的三行 i++ 編譯而成的組語外,還多包含了五行指令: ``` <foo>: push %rbp # 這裡 mov %rsp,%rbp # 這裡 #以下三行由 i++ 編譯而來 mov 0xc7eb54(%rip),%eax # 107f034 <i> add $0x1,%eax mov %eax,0xc7eb4b(%rip) # 107f034 <i> nop # 這裡 pop %rbp # 這裡 retq # 這裡 ``` 而 func_call 這多的五行指令就是造成其執行時間較 macro 的原因。 而觀察objdump輸出的同時也能看出,空間的佔用大小, macro 將每一個 foo() 都編譯程三行組語代碼; func_call 將每個 foo() 都編譯兩行組語代碼,外加一個八行組語代碼的空間存放 foo 函式本身。結果就是每一次的 foo() 呼叫增加 macro 和 func_call 的代碼大小差異,<u>呼叫愈多次 foo(),macro 會比func_call 大得愈多</u>。 以上的結果證實了一開始的推論是正確的。 :::warning 編譯器最佳化可將 `static inline` 的函式做適度地展開,可對照實驗。另外,像是 gcc 和 clang/llvm 這樣現代的編譯器設計中,有 [LTO (link time optimization)](https://eklitzke.org/how-gcc-lto-works),對上述 function inling 有更好的表現 :notes: jserv ::: 參考來源:自己 ## 2. Linux 應用 linked list 在哪些場合?舉三個案例並附上對應程式碼,需要解說,以及揣摩對應的考量 ## 3. GNU extension 的 typeof 有何作用?在程式碼中扮演什麼角色? ### typeof 的作用 typeof 有三種樣貌,分別為 typeof 、 \_\_typeof__ 、\_\_typof。解釋他們區別之前要先知道 GNU C 和標準 C 的差別: * 標準 C:簡單來說就是被某個組織定義出來的C, ANSI C ,ISO C, Standard C C89 C99 C11這些其實都是標準C,只是不同時間定義出來的不同版本。 * GNU C:GNU以標準C為基礎,擴增其他功能實作出來的C。而typeof就是GNU C做的其中一個擴充。 所以如果要標準C的編譯器去認得這些擴充,就必須要在這些擴充的功能名稱前面加上兩個底線「\_\_ 」, \_\_typeof__ 和 \_\_typeof 就是這樣來的。所以三者的功能其實一樣,就是編譯器會在編譯階段將 typeof(n) (或其他兩者)換成 n 的資料型態,也就是說若 n 為一 char 的變數名稱,那 typeof(n) 就會被換成 char。 下方有個測試能看出實際狀況: ```clike // test.c #include<stdio.h> int main() { int a; __typeof__(int) b; &a == &b; __typeof(int) c; &a == &c; typeof(int) d; &a == &d; return 0; } ``` 會將兩變數的位址做比較是因為若兩者的類型不一樣,就會丟出 `warning: comparison of distinct pointer types lacks a cast `的信息 ,所以如果沒有丟出,就表示typeof有照著預期的行為走。 現在將他編譯,因為我的GCC版本為5.4.0,所以若不設定參數,預設是使用 gnu11 的標準: ```shell $gcc test.c $ ``` 結果並沒有跳出任何訊息,表示編譯器有順利地將 typeof(int) 換成 int 。現在試試看用 C99 標準的 C 編譯看看: ```shell= test.c: In function ‘main’: test.c:14:5: warning: implicit declaration of function ‘typeof’ [-Wimplicit-function-declaration] typeof(int) d; ^ test.c:14:12: error: expected expression before ‘int’ typeof(int) d; ^ test.c:14:17: error: expected ‘;’ before ‘d’ typeof(int) d; ^ test.c:15:12: error: ‘d’ undeclared (first use in this function) &a == &d; ^ test.c:15:12: note: each undeclared identifier is reported only once for each function it appears in ``` #2果然跳出 warning 訊息,警告說 typeof 沒有被定義。 ### typeof 在程式碼中扮演的角色 typeof 被大量運用在 macro 之中,因為 macro 本身並不像函式一樣,能限制參數的資料型態,它只是很單純的做程式碼的代換或展開,但如果在 macro 中使用 typeof 宣告出和參數擁有一樣型態的變數,然後再用利用上述提到的不同指標間比較會丟出 warning的技巧,就可以在 macro 的參數不符合期待時,丟出 warning信號。以下是 Linux kernel 中運用的例子: ```clike //linux/include/linux/overflow.h /* * For simplicity and code hygiene, the fallback code below insists on * a, b and *d having the same type (similar to the min() and max() * macros), whereas gcc's type-generic overflow checkers accept * different types. Hence we don't just make check_add_overflow an * alias for __builtin_add_overflow, but add type checks similar to * below. */ #define check_add_overflow(a, b, d) ({ \ typeof(a) __a = (a); \ typeof(b) __b = (b); \ typeof(d) __d = (d); \ (void) (&__a == &__b); \ (void) (&__a == __d); \ __builtin_add_overflow(__a, __b, __d); \ }) #define check_sub_overflow(a, b, d) ({ \ typeof(a) __a = (a); \ typeof(b) __b = (b); \ typeof(d) __d = (d); \ (void) (&__a == &__b); \ (void) (&__a == __d); \ __builtin_sub_overflow(__a, __b, __d); \ }) ``` 註解部份也有提到,在做 overflow 的檢查前,他們會先確定 a 、 b 、 \*d的 type 一樣。為了達到這目的,會先利用 typeof 宣告類型和 a 一樣的 __a 、 類型和 b 一樣的 __b 、類型和 d 一樣的 __d ,並拿 &__a 和 &__b 比較、 &__a 和 __d,若 a 、 b 和 *d 類型一樣,那就不會丟出 warning 訊息。 參考來源: 1. [Referring to a Type with typeof ](https://gcc.gnu.org/onlinedocs/gcc-3.3/gcc/Typeof.html) 2. [\_\_typeof__() 、 __typeof() 、 typeof()的區別](https://my.oschina.net/u/2472425/blog/1083157) ## 4. 解釋巨集原理 #define container_of(ptr, type, member) \\ \_\_extension__({ \\ const \_\_typeof\_\_(((type *) 0)->member) *__pmember = (ptr); \\ (type *) ((char *) __pmember - offsetof(type, member)); \\ }) 這巨集分為以下兩個步驟: __1. const \_\_typeof\_\_(((type *) 0)->member) *\_\_pmember = (ptr);__ 這裡宣告一個指標 __pmember ,其型態和 type 的 member 成員一樣,然後將 ptr 的值指定給 __pmemeber。 從這裡就可以得到幾個訊息: * type 的型態必須是 struct ,否則會丟出類似以下錯誤: `error: request for member ‘$(member)’ in something not a structure or union` * member 必須是 type 的成員。若不是的話會丟出類似以下錯誤: `error: ‘$(type)’ has no member named ‘$(member)’` * ptr 的類型要跟 type 的 member 成員的類型一樣,否則會出現類似以下警告: `warning: initialization from incompatible pointer type` __2. (type *) ((char *) \_\_pmember - offsetof(type, member));__ 先來看看 offsetof 的作用,以下為 wiki 的解釋: `It evaluates to the offset (in bytes) of a given member within a struct or union type, an expression of type size_t.` 也就是說 offsetof(type, member) 會得到 member 在 type 中的偏移量。另外因為有上述第一步驟,所以可以確定 member 一定是 type 的成員。 所以 contain_of 第二步驟就是拿這個「值和 ptr 一樣」的 __pmember 去扣掉 member 在 type 中的相對位置,<u>結果會得到 ptr 所在的 struct 的記憶體起始位置</u>。以下為舉例和圖解。 現在有一個 struct 定義與宣告如下: ```clike struct Node { int data; struct list_head list; } node; ``` 另外我透過別的 list_head 的 next 指標知道 node 底下的 list 記憶體位置(假定其為 node_list , 型態為 struct list_head* )。 node 的記憶體配置示意圖如下(64位元系統): ![](https://i.imgur.com/TEKStxZ.png) 這時若我想用 node_list 反推 node 的記憶體位置,就可以使用 contain_of(node_list, struct Node, list): 首先第一步驟會宣告一個 __pmember,方法如下: `const __typeof__(((struct Node*)0)->list) __pmember = node_list;` __pmember 其實就是 node_list的分身,他們有一樣的型態、指向一樣的記憶體位置。 第二步驟則是: `(struct Node *) ((char *) __pmember - offsetof(struct Node*, list));` 由上方的 node 記憶體配置圖可以知道, list 在 node 中的偏移量為 8 ,也就是說 offsetof(struct Node*, list) 等於 8 。而 __pmember 指向的是 X+8 ,扣掉偏移量 8 後,就會得到 node 的記憶體起始位置。 ## 5. 除了你熟悉的 add 和 delete 操作,list.h 還定義一系列操作,為什麼呢?這些有什麼益處? list.h 定義許多 macro 來打包許多特定行為,像是 list_for_each 用來走遍 list 內所有資料、 list_first_entry 用來取得 list 的第一筆資料等等。這些打包起來的 macro 能讓許多行相通目的的程式碼變成單行 macro ,提供開發人員更直觀、更高層次的方式來維護與開發。除此之外,也較能減少錯誤的出現。 ## 6. LIST_POISONING 這樣的設計有何意義? 在 list.h 中若 LIST_POISONING 啟動,被刪除的 struct list_head 實體的 next 和 prev 就會指向一個 kernel 無法存取的記憶體位置。在這個指定的過程中是沒有問題的,不論是編譯階段或是執行階段都不會有錯誤出現,但是當有人試著對這段記憶體做操作時,就會出現記憶體區段錯誤的信息。見以下範例: ```clike // poison_test.c #include<stdio.h> #define POISON 0x00100100 int main() { int* a = (int *) (0x00100100); printf("Next step will do '*a = 0'\n"); *a = 10; return 0; } ~ ``` 編譯與執行結果: ```shell $gcc poison_test.c $./a.out Next step will do '*a = 0' 程式記憶體區段錯誤 (core dumped) ``` 將被從 list 中移除的 list_node實體的 next 和 prev 指向這麼一個非法的記憶體位置,就能夠確保他們不會被使用,一旦真的被使用,就會出現錯誤。 ## 7. linked list 採用環狀是基於哪些考量? ## 8. 什麼情境會需要對 linked list 做排序呢?舉出真實世界的應用,最好在作業系統核心出現 ## 9. 什麼情境會需要需要找到 第 k 大/小元素 呢?又,你打算如何實作? ## 10. list_for_each_safe 和 list_for_each 的差異在哪?“safe” 在執行時期的影響為何? ```clike= /** * list_for_each - iterate over list nodes * @node: list_head pointer used as iterator * @head: pointer to the head of the list * * The nodes and the head of the list must must be kept unmodified while * iterating through it. Any modifications to the the list will cause undefined * behavior. */ #define list_for_each(node, head) \ for (node = (head)->next; node != (head); node = node->next) /** * list_for_each_safe - iterate over list nodes and allow deletes * @node: list_head pointer used as iterator * @safe: list_head pointer used to store info for next entry in list * @head: pointer to the head of the list * * The current node (iterator) is allowed to be removed from the list. Any * other modifications to the the list will cause undefined behavior. */ #define list_for_each_safe(node, safe, head) \ for (node = (head)->next, safe = node->next; node != (head); \ node = safe, safe = node->next) ``` list_for_each_safe 會同時記住當下的 node 和下一個 node,這樣做的目的其實註解裡也有提到,這樣能夠允許當下的 node 被刪除,因為下一個已經被 safe 記下,不會因為當下的 node 被移除,而遺失指向下一個 node 的指標。所以 safe 能夠降低執行時期發生 Memory leak 的可能。 ## 11. for_each 風格的開發方式對程式開發者的影響為何? 提示:對照其他程式語言,如 Perl 和 Python ## 12. 程式註解裡頭大量存在 @ 符號,這有何意義?你能否應用在後續的程式開發呢? (提示: 對照看 Doxygen) @ 是個讓 Doxygen 抓取的標籤特徵。只要熟悉 Doxygen 的分析規則,在後續的程式開發中都依照其規則撰寫註解,這樣就能隨時利用 Doxygen 來快速地產生程式說明檔。 Doxygen 是一個開發輔助工具,他能就將程式內部特定格式的註解轉成 html 等特定檔案格式,自動生成程式碼的說明文件。而 @ 就是剛剛所提到的特定格式。 舉例來說,若 doxygen 讀到以下格式的註解: ```clike /** @mainpage This is the mainpage. */ ``` 那 doxygen 就會在輸出文件中建立一頁 mainpage ,內文為「 This is the mainpage. 」。 ![](https://i.imgur.com/LtETb5R.png) 而這所謂的特定註解格式其實就是用 __/**__ 和 __*/__ 包含在內的註解。然後在這樣格式的註解內容就可以使用許多 doxygen 預設的標籤,用法跟 html 的標籤有點像,只是名字不一樣,也不需要結束標籤。所以回到剛剛 mainpage 的例子,這例子的標籤就是 @mainpage ,當 doxygen 讀到這個標籤,他就會建立一個 mainpage ,然後把同註解內後面全部的內容都放入其中。而那個 @ 就是 doxygen 的標籤特徵,另外除了 @ 外, \ 也能達到一樣的效果,也就是說 @mainpage 和 \mainpage是一樣的。 現在來看一個比較完整的例子: ```clike= // doxygen_test.c /** @file Here is the Detail Description. */ #include<stdio.h> /** @mainpage This is a main page. Here is a blod @b word. */ int global; /** This is a global var. */ /** Here should be the description of foo() @param i This number will increase by 1. */ void foo(int* i) { (*i)++; } /** Here should be the description of main() */ int main() { int i = 0; foo(&i); printf("i = %d\n", i); return 0; } ``` 針對上面範例做一點說明: @file :標示該文件給 doxygen 標示,如果文件開頭沒有 @file 標籤,那就算該文件在 doxygen 的輸入資料夾內, doxygen也會選擇忽略。 @b :能讓此標籤後面的一個單字在輸出說明檔中變成粗體字。 @parm:函式或變數上方的註解會被 doxygen 認為是在解釋該函式或變數,而 @parm 後方的單字會被認為是該函式的參數,單字後方的一句話則會被當成是該參數的說明。 現在要來用 doxygen 產生上方 doxygen_test.c 的說明文件: ```shell= $ls doxygen_test.c $doxygen -g $ls Doxyfile doxygen_test.c $doxygen Doxyfile Doxyfile doxygen_test.c html latex $gnome-open html/index.html ``` #3 會在工作目錄下產生 Doxyfile 的預設設定檔 Doxyfile ,內有許多設定可以修改,但這裡示範就直接使用預設值,而預設值就是分析工作目錄內所有的程式碼,並將結果輸出到工作目錄下。 #5 指令是使用叫 doxygen 用 Doxyfile 內的設定值開始分析註解並且產出說明檔,共會產出兩個資料夾 html 和 latex。 #8 點選 html 內的 index.html 就能用瀏覽器來看 doxygen_test.c 的說明檔了。 結果如下: ![](https://i.imgur.com/0qfWbYm.png) ![](https://i.imgur.com/jcrjbcy.png) 參考來源: 1. [Doxygen - All Commands簡易說明(未完成) | 給姆喇低賽](https://wingedrobin.blogspot.com/2015/03/doxygen-all-commands.html) 2. [21世紀C語言之27: doxygen](https://ithelp.ithome.com.tw/articles/10161530) ## 13. tests/ 目錄底下的 unit test 的作用為何?就軟體工程來說的精神為何? ## 14.tests/ 目錄的 unit test 可如何持續精進和改善呢? ## Merge Sort 程式碼:[merge_sort.c](https://github.com/ChihAnLin0607/linux-list/blob/master/merge_sort/merge_sort.c) 思路: 1. 讀取cities.txt內的九萬多筆資料,並用此建立 testlist 。 2. 同時用陣列 values 存取一樣內容、一樣順序的資料。 3. 利用 merge sort 演算法排序 testlist 內的資料。 4. 使用 qsort 排序 values 內的資料。 5. 依順序一一比較兩者內資料順序是否一致。是的話表示 merge sort 成功。 空間利用: * 為了節省空間,所以一筆資料只建立一個記憶體實體,不論是 testlist 中的 item 或是 values 都只是紀錄該筆資料的記憶體位置而已。 * 這樣的好處除了節省空間外,在最後一步驟 testlist 和 values 內資料比較時,也可以直接比較其指標指向的記憶體位置是否一樣就好,而不用真的比較其字串內容。 時間: 將 Dict 內的測試當成輸入,比較資料由少到高的所需時間變化: ![](https://i.imgur.com/6JCVq1b.png) 可以看出雖然資料數量由一增加到九萬多,但所花時間只增加到 0.045 秒左右而已。符合時間複雜度 $O(nlgn)$ 的結果。

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully