gpwork4u
    • 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
    # 2020q1 Homework1 (lab0) contributed by < `gpwork4u` > ## 實驗環境 ```shell $ uname -a Linux 5.3.0-28-generic #30~18.04.1-Ubuntu SMP Fri Jan 17 06:14:09 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux $ gcc --version gcc (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0 ``` ## 作業要求 - 詳細閱讀 [C Programming Lab](http://www.cs.cmu.edu/~213/labs/cprogramminglab.pdf) ,依據指示著手修改 `queue.[ch]` 和連帶的檔案,測試後用 Git 管理各項修改,記得也該實作 `q_sort` 函式 - 修改排序所用的比較函式,變更為 `natural sort`,在 “simulation” 也該做對應的修改,得以反映出 `natural sort` 的使用。 - 運用 Valgrind 排除 qtest 實作的記憶體錯誤,並透過 Massif 視覺化 “simulation” 過程中的記憶體使用量,需要設計對應的實驗 - 研讀論文 Dude, is my code constant time?,解釋本程式的 “simulation” 模式是如何透過以實驗而非理論分析,達到驗證時間複雜度,需要解釋 Student’s t-distribution 及程式實作的原理; - 解釋 select 系統呼叫在本程式的使用方式,並分析 console.c 的實作,說明其中運用 CS:APP RIO 套件 的原理和考量點。可對照參考 CS:APP 第 10 章重點提示 :arrow_forward: 為避免「舉燭」,請比照 qtest 實作,撰寫獨立的終端機命令解譯器 (可建立新的 GitHub repository) ## 開發過程 ### queue.h ```cpp typedef struct { list_ele_t *head, *tail; int size; } queue_t; ``` 根據老師提供的作業說明所作的修改,加上一個 int size 變數在每次增減元素時做紀錄。 在實作```q_insert_tail```時,要求要將原本 $O(n)$ 的時間修改為 $O(1)$ ,因此在 ```queue_t``` 中增加一個 ```tail``` 指標,以便直接在 queue 的尾端新增元素。 參考 [OscarShiang ](https://hackmd.io/@WaryvM_MTuOkXtEEalFsvQ/Sy5oUP6MU#%E5%AF%A6%E4%BD%9C-q_reverse) 同學指標的使用方法,改用 `list_ele_t *tmp` 指標進行操作。 ### queue.c #### q_new - 回傳一個空的 queue - 當 malloc 失敗時需要回傳 NULL ```c queue_t q_new() { queue_t *q = malloc(sizeof(queue_t)); if (!q) return NULL; q->head = NULL; q->tail = NULL; q->size = 0; return q; } ``` #### q_size - return queue 的元素數量 - queue 為 NULL 時 return 0; ```c int q_size(queue_t *q) { if (!q) return 0; return q->size; } ``` 把 ```q->size``` 直接傳回去就是 一開始實作的時候沒考慮 queue 為 NULL 的情形,加進去之後分數會多六分 #### q_free - 將 queue 中佔用的記憶體釋放 - 當 queue 為 NULL 時,直接 return 結束 ```c void q_free(queue_t *q) { if (!q) return; while (q->head) { list_ele_t *tmp; q->head = q->head->next; free(tmp->value); free(tmp); } free(q); } ``` 從 head 依序拜訪各個元素並作釋放 最後再將 queue 的指標釋放 #### q_insert_head - 從 queue 的 head 插入一個元素 - 當 queue 為 NULL 或分配位置時失敗 return false ```c bool q_insert_head(queue_t *q, char *s) { list_ele_t *newh; if (!q) return false; newh = malloc(sizeof(list_ele_t)); if (!newh) return false; newh->value = malloc(sizeof(char)*(strlen(s) + 1)) if (!newh->value) { free(newh); return false; } snprintf(newh->value, strlen(s) + 1, "%s", s); newh->next = q->head; q->head = newh; if (!q->tail) q->tail = newh; q->size++; return true; } ``` 在 queue 為空時需要另外將 tail 也指向新增的元素 於作業說明中提到 >依據 [CERN Common vulnerabilities guide for C programmers](https://security.web.cern.ch/security/recommendations/en/codetools/c.shtml) 建議而移除 gets / sprintf / strcpy 等不安全的函式; 因此在將資料傳給 newh->value 時為了避免使用 strcpy ,改採用根據 CERN 所提供的建議中的snprintf函式來進行操作。 >**int snprintf(char * restrict s, size_t n, const char * restrict format, ...);** 在使用snprintf時,將參數 ```size_t n``` 設為 ```strlen(s)```時,會發現輸出結果跟預期的不一樣,如下 ``` shell cmd> new q = [] cmd> ih 123 cmd> ih 123 q = [12] ``` 在經查閱[C99規格書](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf)之後,其對於 snprintf的敘述如下 > The snprintf function is equivalent to fprintf, except that the output is written into an array (specified by argument s) rather than to a stream. If n is zero, nothing is written, and s may be a null pointer. Otherwise, output characters beyond the n-1st are discarded rather than being written to the array, and a null character is written at the end of the characters actually written into the array. If copying takes place between objects that overlap, the behavior is undefined. 從中可以知道, ```snprintf``` 會根據 ```size_t n``` ,將第 n-1 個位置以後的字串捨棄,並在第 n 位放入 NULL,因此在利用 strlen(s) 作為 Buffer size 時,須加一才能將完整的 s 值傳進 ```newh->value``` 。 在 value 做 malloc 的時候,一開始是使用 ```sizeof(s)``` 配置記憶體空間,後來在後續的測試中發現傳入的字串大小大於等於 8 時,在進行釋放的時候會出現。 > ERROR: Corruption detected in block with address 0x558b91c49ce0 when attempting to free it 後來測試的時候發現 ```malloc(sizeof(s))``` 並不會配置一個跟 ```s``` 一樣的記憶體大小,而是 pointer 大小,導致在只用 ```sizeof(s)``` 作分配時不夠大,在用```snprintf``` 放入字串過大導致在釋放記憶體時動到了原本沒分配的部份,最後改用 ```sizeof(char)*strlen(s)+1``` 就可解掉這個 error,在 ```q_insert_tail``` 更新了一樣的作法。 根據 C 規格書中對 sizeof 的描述,如傳入的 sizeof(s) 中 s 是個陣列的話,是可以直接用的,當是 pointer 時就只會是 pointer 的大小,而在 64 位元的系統中,一個 pointer 的大小即是8個 byte 。 > When sizeof is applied to an operand that has type char, unsigned char, or signed char, (or a qualified version thereof) the result is 1. When applied to an operand that has array type, the result is the total number of bytes in the array. >103) When applied to an operand that has structure or union type, the result is the total number of bytes in such an object, including internal and trailing padding. #### q_insert_tail - 從 queue 的尾端新增一個元素 - 成功return true - 若 queue 為 NULL 或分配位置失敗時則 return false - 時間複雜度須為 $O(1)$ ,即不論 queue 的長度為何執行時間,操作步驟為固定數量。 ```c bool q_insert_tail(queue_t *q, char *s) { list_ele_t *newh; if (!q) return false; newh = malloc(sizeof(list_ele_t)); if (!newh) return false; newh->value = malloc(sizeof(char)*(strlen(s) + 1)) free(newh); return false; } snprintf(newh->value, strlen(s) + 1, "%s", s); newh->next = NULL; q->size++; if (!q->tail) { q->head = newh; q->tail = newh; return true; } q->tail->next = newh; q->tail = newh; return true; } ``` 在 queue 為空時需要另外將 head 也指向新增的元素 #### q_remove_head - 將 queue head 刪除 - 成功刪除 return true - 當 queue 為空或 NULL 時 return false - 若 ```sp``` 不是 NULL 且 remove 執行成功時要將被 remove 的元素值的前bufsize - 1 位放進 ```sp``` 中,並且 sp 的最後一位須為 null terminator。 ```cpp bool q_remove_head(queue_t *q, char *sp, size_t bufsize) { if(!q || !q->head) return false; list_ele_t *tmp; tmp = q->head; if (sp) { snprintf(sp, bufsize, "%s", tmp->value); } q->head = q->head->next; free(tmp->value); free(tmp); q->size--; return true; } ``` 在將被 remove 值放進 sp 中,要求要取被 remove 的元素值前 bufsize - 1 位,在 insert 時用到的 ```snprintf```,剛好就可以拿來使用。 #### q_reverse - 將 queue 的內容反轉 - queue 為 NULL 或 empty 時直接結束 - 不能 allocate 新的記憶體位址和 free 原有的記憶體位址 ``` c void q_reverse(queue_t *q) { if (!q) return; if (q->size <= 1) return; list_ele_t *tmp; q->tail->next = q->head; while (q->head->next != q->tail) { tmp = q->head->next; q->head->next = tmp->next; tmp->next = q->tail->next; q->tail->next = tmp; } q->tail = q->head; q->head = q->head->next; q->tail->next = NULL; } ``` 除了 queue 為 NULL 和 empty 時,```q->size``` 為 1 時也可以直接結束。 一開始的想法是用拆一個接一個的方式從 ```q->head``` 依序將元素拆掉並重新反向接上,在接完之後需要另外將 ```q->tail``` 歸位。 後來想到先把 ```q->tail``` 接到 ```q->head``` 上,並依序將 ```q->head``` 到 ```q->tail``` 之間的元素接到 ```q->tail``` 後面,最後只須將```q-tail``` 和 ```q->head``` 互換位置,再將```q->tail->next``` 接到 NULL 即可,示意圖如下。 ```graphviz digraph linked_list{ rankdir=LR; node [shape=record]; a [label="{ <data> a | <ref> }", width=1.2] b [label="{ <data> b | <ref> }"]; c [label="{ <data> c | <ref> }"]; a:ref:c -> b:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, arrowsize=1.2]; b:ref:c -> c:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; c:ref:c -> a :data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; head [shape=box]; head -> a; tail [shape=box]; tail -> c; } ``` ```graphviz digraph linked_list{ rankdir=LR; node [shape=record]; a [label="{ <data> a | <ref> }", width=1.2] b [label="{ <data> b | <ref> }"]; c [label="{ <data> c | <ref> }"]; a:ref:c -> c:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, arrowsize=1.2]; b:ref:c -> a:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; c:ref:c -> b :data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; head [shape=box]; head -> a; tail [shape=box]; tail -> c; } ``` ```graphviz digraph linked_list{ rankdir=LR; node [shape=record]; a [label="{ <data> a | <ref> }", width=1.2] b [label="{ <data> b | <ref> }"]; c [label="{ <data> c | <ref> }"]; a:ref:c -> NULL [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, arrowsize=1.2]; b:ref:c -> a:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; c:ref:c -> b :data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; head [shape=box]; head -> c; tail [shape=box]; tail -> a; } ``` #### q_sort - 將 queue 中元素小到大做排序 - 使用 natual sort (未完成) ``` cpp void q_sort(queue_t *q) { if (!q) return; if (q->size <= 1) return; merge_sort(&(q->head)); while (q->tail->next) q->tail = q->tail->next; } ``` ##### sort.c ``` cpp #include <stdio.h> #include <string.h> #include "queue.h" void split(list_ele_t *source, list_ele_t **pre_head, list_ele_t **post_head) { list_ele_t *fast, *slow; fast = source->next; slow = source; while (fast) { fast = fast->next; if (fast) { fast = fast->next; slow = slow->next; } } *pre_head = source; *post_head = slow->next; slow->next = NULL; } list_ele_t *merge(list_ele_t *pre_head, list_ele_t *post_head) { list_ele_t *start, *current; start = pre_head; pre_head = pre_head->next; current = start; while (pre_head && post_head) { if (strcmp(pre_head->value, post_head->value) > 0) { current->next = post_head; post_head = post_head->next; } else { current->next = pre_head; pre_head = pre_head->next; } current = current->next; } if (pre_head) current->next = pre_head; else if (post_head) current->next = post_head; return start; } void merge_sort(list_ele_t **head) { list_ele_t *pre_head, *post_head; if (!(*head)) return; if (!(*head)->next) return; split(*head, &pre_head, &post_head); merge_sort(&pre_head); merge_sort(&post_head); if (strcmp(pre_head->value, post_head->value) <= 0) { *head = merge(pre_head, post_head); } else { *head = merge(post_head, pre_head); } } ``` 採用 merge sort 進行排序,即使在 worst case,merge sort 也是 $O(nlog{n})$ 的時間複雜度。實作的部份參考 [Linked List Sort](https://npes87184.github.io/2015-09-12-linkedListSort/) :::danger TODO: 1. 在 `merge` 和 `merge_sort` 這二個函式都呼叫到 `strcmp`,也就是 comparator,倘若想更換後者為其他函式 (或傳遞函式指標),那就需要在這兩個函式內容變更,不僅不便利,還會隱藏風險; 2. `split` 函式通用性不足,可改為巨集或 inline function 實作; 3. 考慮到 `merge_sort` 函式原型宣告的變更: ```cpp static list_ele_t *merge_sort(list_ele_t *head); ``` 輸入原本的 head,回傳因為排序而得到新的 head,在實作和使用上都會更便利; :notes: jserv ::: ## 參考資料 * [2020 春季 lab0c 作業說明](https://hackmd.io/@sysprog/linux2020-lab0) * [C Programming Lab: Assessing Your C Programming Skills](http://www.cs.cmu.edu/~213/labs/cprogramminglab.pdf) * [C99 規格書](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf) * [C11 規格書](https://web.cs.dal.ca/~vlado/pl/C_Standard_2011-n1570.pdf) * [Drawing Graphs using Dot and Graphviz](http://www.tonyballantyne.com/graphs.html)

    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