Shengyuu
    • 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
    • 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 Versions and GitHub Sync Note Insights 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
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    # 2019q3 Homework2 (lab0) contributed by < `Shengyuu` > ## 說明 - [作業要求](https://hackmd.io/s/BJA8EgFB4) - [C Programming Lab](http://www.cs.cmu.edu/~213/labs/cprogramminglab.pdf) ## 實驗環境 ```shell $ uname -a Linux yao 4.15.0-45-generic #48-Ubuntu SMP Tue Jan 29 16:28:13 UTC 2019 x86_64 x86_64 x86_64 GNU/Linu $ gcc --version gcc (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0 ``` ## 作業需求 ### 實作 FIFO & LIFO Queue - 要求 - `q_new`: 新增一空的 queue - `q_free`: 清除 queue - `q_insert_head`: 在 queue 的頭插入一元素(LIFO) - `q_insert_tail`: 在 queue 的尾插入一元素(FIFO) - `q_remove_head`: 移除 queue 的開頭元素並輸出 - `q_size`: 回傳 queue 有多少個元素 - `q_reverse`: 反轉 queue 中元素的順序 --- 以上節錄自 [C Programming Lab](http://www.cs.cmu.edu/~213/labs/cprogramminglab.pdf) - 注意 - 傳入的 queue 是否為 NULL - malloc 是否成功 - malloc 後記憶體的歸還 - `q_insert_tail` & `q_size` 的複雜度要是 $O(1)$ ## 實作 ### 程式碼說明: - ### `queue.h` - 為了達成作業要求,讓 `q_insert_tail` & `q_size` 的複雜度為 $O(1)$,在 struct 裡面新增 head, size 兩元素 ```clike=+ typedef struct ELE { char *value; struct ELE *next; } list_ele_t; ``` - ### `queue_new()` - 新建一大小為 0 的 queue ,且 head、tail皆指向 NULL - **若 malloc 失敗要回傳 NULL** ```cpp queue_t *q_new() { queue_t *q = malloc(sizeof(queue_t)); /* What if malloc returned NULL? */ if (!q) return NULL; else { q->head = NULL; q->tail = NULL; q->size = 0; return q; } } ``` :::danger 注意程式碼縮排風格是 4 個空白字元和 K&R,連同共筆列出的程式碼也需要變更 :notes: jserv ::: - ### `q_free()` - queue 為 element 陣列,而 element 陣列中含有一 char 陣列,因此在清除 queue 時需將此兩陣列的空間歸還 - **注意傳入的 queue 是否為 NULL** - **若該 element 的 value 為 NULL 則不能 free** ```clike=+ void q_free(queue_t *q) { if (q) { list_ele_t *tmp = q->head; while (q->head) { if (q->head->value) free(q->head->value); // avoid free NULL q->head = q->head->next; free(tmp); tmp = q->head; } /* Free queue structure */ free(q); } } ``` - ### `q_insert_head()` - 為了將傳近來的 char 陣列 s 插入 queue 中,我們必須新建一個 list_ele_t 的實體 newh ,將 s 複製進 newh 後再將 newh 插入 queue 的開頭 - 新建實體,而用到了 malloc 動態配置記憶體,注意若陣列 s 是空的,則要將 newh 分配的記憶體歸還回去 :::warning 注意:程式碼的排版有明確要求,在 HackMD 和 GitHub 都嚴格遵守,請留意。需要變更本共筆所有程式列表 (可自 GitHub 的分支上複製) :notes: jserv ::: > 已更改 > [name=Shengyuu] ```clike=+ bool q_insert_head(queue_t *q, char *s) { if (!q) { printf("The queue is NULL\n"); return false; } /* What should you do if the q is NULL, malloc is NULL and s is NULL? */ list_ele_t *newh = malloc(sizeof(list_ele_t)); /* fail to malloc*/ if (!newh) { printf("Fail to allocate list\n"); return false; } newh->value = malloc(strlen(s) + 1); /* fail to malloc*/ if (!newh->value) { printf("Fail to allocate value\n"); free(newh); return false; } strcpy(newh->value, s); /* queue is empty before insert*/ if (q->size == 0) { newh->next = NULL; q->size++; q->head = newh; q->tail = newh; return true; } else { newh->next = q->head; q->head = newh; q->size++; return true; } } ``` > 一開始將三種需要 return false 的可能寫在同一個 if case 裡面統一判斷,但 qtest 一直出現 error 訊息,後來才想到若第一次 malloc 給 newh 記憶體 失敗的話,第二次 malloc 給 newh->value 記憶體就會是個有問題的寫法 - ### `q_insert_tail()` - 此函式須注意的地方和上一個基本上都差不多,因此不再贅述 ```clike=+ bool q_insert_tail(queue_t *q, char *s) { /* You need to write the complete code for this function */ /* Remember: It should operate in O(1) time */ if (!q) { printf("The queue is NULL\n"); return false; } list_ele_t *newt = (list_ele_t *) malloc(sizeof(list_ele_t)); if (!newt) { printf("Fail to allocate list\n"); return false; } newt->value = malloc(strlen(s) + 1); if (!newt->value) { printf("Fail to allocate value\n"); free(newt); return false; } strcpy(newt->value, s); if (!q->size) { q->size++; newt->next = NULL; q->head = newt; q->tail = newt; return true; } else { q->size++; newt->next = NULL; q->tail->next = newt; q->tail = newt; // error return true; } } ``` ◎**第一次完成這個 function 時只記得做 q->tail->next = newt ,而忘了 q->tail = newt ,造成tail都沒有被更新到** - ### `q_remove_head()` - 這裡除了要檢查傳入的 queue 是否為 NULL 之外,還要多檢查 q->head 是否為空,一樣要注意不能將這些檢查工作放在同一個 if case 內,若 q 為 NULL 則 q->head 就會是個有問題的指令 - 按照作業的要求,即使傳入的 sp 為 NULL ,我們還是需要將 queue->head 做移除的動作 - 這裡一樣記得要作兩次 free 的動作,將 value、element 的空間都歸還 ```clike=+ bool q_remove_head(queue_t *q, char *sp, size_t bufsize) { if (!q) { printf("The queue is NULL\n"); return false; } if (!q->head) { printf("The head is NULL\n"); return false; } if (!sp) { printf("sp is NULL\n"); } if (sp) { strncpy(sp, q->head->value, bufsize - 1); sp[bufsize - 1] = '\0'; } if (q->size == 1) { q->size--; free(q->head->value); free(q->head); q->head = NULL; q->tail = NULL; return true; } else { list_ele_t *tmp = q->head; q->size--; q->head = q->head->next; free(tmp->value); free(tmp); return true; } } ``` ◎ **在做 free(q->head->value) 的時候若 value 的值為 NULL 應該未出問題**----待修改 - ### `q_size()` - 檢查傳入的 queue 是否為 NULL,若否則直接回傳 size ```clike=+ int q_size(queue_t *q) { /* You need to write the code for this function */ /* Remember: It should operate in O(1) time */ if (!q) return false; return q->size; } ``` - ### `q_reverse()` - 對 queue 做翻轉,需要修改 queue 中的兩個元素,分別為 head、tail ,而修改 head、tail 指向的 element 時只需修改其中的 next ,不需要修改 value 的部份。 - 因為我們設計的 element 裡面只有存取下一筆資料的位置(next),而沒有存取上一筆資料,所以在將資料拿出的時候一定得從開頭開始拿,也因此在做 reverse 的時候我們需要以"這筆資料的下一筆資料的 next 會指向這筆資料"的邏輯去實踐 ```clike=+ void q_reverse(queue_t *q) { if (!q) { printf("The queue is NULL\n"); } else { int count = q->size; if (count > 0) { q->tail = q->head; list_ele_t *tmp_ele; list_ele_t *tmp_ele_next; tmp_ele = NULL; while (count > 0) { tmp_ele_next = tmp_ele; tmp_ele = q->head; q->head = q->head->next; tmp_ele->next = tmp_ele_next; count--; } q->tail->next = NULL; q->head = tmp_ele; } else printf("queue is empty\n"); } } ``` ◎ **剛開始完成這段程式碼的時候是將 q->head = q->head->next 寫在下面一行 tmp_ele... 的後面,編譯後 qtest 顯示 error ,回去檢查程式碼才發現 q->head... 一定要寫在前面,因為此時的 tmp_ele 和 q->head 是除存同一個記憶體位置的,若先將 tmp_ele->next 改掉,就沒辦法找到我們要的下一筆資料了** :::warning 避免用「噴出」這種語意不明的詞彙,改為「qtest 顯示...」 :notes: jserv ::: >已更改 >[name=Shengyuu] ### 驗證程式碼: ``` $make test ``` ![](https://i.imgur.com/jMfTc7k.png) ## 自動評分系統 ### Makefile 在終端機上輸入 `$make test` 即可使用自動評分系統,第一步先去 Makefile 內看 `test` 做了什麼事 ``` test: qtest scripts/driver.py scripts/driver.py ``` 從這裡可以看出來當我們在下指令 `make test` 的時候其實是去執行 `scripts/driver.py` ,下一步去看 `driver.py` 做了些什麼 ### driver 在 `driver.py` 中主要執行 test 的部份都被包在 class Tracer 內, `run` 函式只負責處理參數、創建 class Tracer 和呼叫 Tracer 內的 `run` 函式 ,下面程式碼是 `run` 函式創建 class Tracer 程式碼 ``` t = Tracer(qtest = prog, verbLevel = vlevel, autograde = autograde, useValgrind = useValgrind) t.run(tid) ``` #### class Tracer Tracer 最前面將測試檔案的路徑以及每小題的分數都存在陣列型態的變數中,如下: ```python traceDirectory = "./traces" ... ... ... traceDict = { 1 : "trace-01-ops", 2 : "trace-02-ops", 3 : "trace-03-ops", 4 : "trace-04-ops", 5 : "trace-05-ops", 6 : "trace-06-string", 7 : "trace-07-robust", 8 : "trace-08-robust", 9 : "trace-09-robust", 10 : "trace-10-malloc", 11 : "trace-11-malloc", 12 : "trace-12-malloc", 13 : "trace-13-perf", 14 : "trace-14-perf", 15 : "trace-15-perf" } ... ... ... maxScores = [0, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7] ``` Trce 的邏輯則是透過物件內的 `run` 函式以 for 迴圈的方式將每筆資料都送進 `runTrace` 函式進行 Trace 的動作,如下: ```python for t in tidList: tname = self.traceDict[t] if self.verbLevel > 0: print("+++ TESTING trace %s:" % tname) ok = self.runTrace(t) maxval = self.maxScores[t] tval = maxval if ok else 0 ``` `runTrace` 函式內透過 `clist` 變數儲存執行 qtest 的命令,並透過 subprocess 的技巧創建一個子進程執行 qtest,程式碼如下: ```python def runTrace(self, tid): if not tid in self.traceDict: print("ERROR: No trace with id %d" % tid) return False fname = "%s/%s.cmd" % (self.traceDirectory, self.traceDict[tid]) vname = "%d" % self.verbLevel clist = [self.command, self.qtest, "-v", vname, "-f", fname] print("%s" % " ".join(clist)) try: retcode = subprocess.call(clist) except Exception as e: print("Call of '%s' failed: %s" % (" ".join(clist), e)) return False return retcode == 0 ``` clist 儲存的命令如下: ``` ./qtest -v 1 -f ./traces/trace-01-ops.cmd ``` ### qtest qtest 才是整個自動評分系統的主體,系統將測試時會用到的 function 都當成一個一個 cmd ,並且給予每個 cmd 獨有的 name 和 documents ,將這些資料都存在 `struct cmd_ptr` 中並用 linked list 結構將所有 cmd 串接起來,`struct cmd_ptr` 如下: ```cpp /* Organized as linked list in alphabetical order */ typedef struct CELE cmd_ele, *cmd_ptr; struct CELE { char *name; cmd_function operation; char *documentation; cmd_ptr next; }; ``` #### cmd 的建立 利用 `add_cmd` 函式將 cmd 串接在全域變數 `cmd_list` , `qtest.c` 提供一些對 queue 進行操作的 cmd 如下: ```cpp static void console_init() { add_cmd("new", do_new, " | Create new queue"); add_cmd("free", do_free, " | Delete queue"); add_cmd("ih", do_insert_head, " str [n] | Insert string str at head of queue n times " "(default: n == 1)"); add_cmd("it", do_insert_tail, " str [n] | Insert string str at tail of queue n times " "(default: n == 1)"); add_cmd("rh", do_remove_head, " [str] | Remove from head of queue. Optionally compare " "to expected value str"); add_cmd( "rhq", do_remove_head_quiet, " | Remove from head of queue without reporting value."); add_cmd("reverse", do_reverse, " | Reverse queue"); add_cmd("size", do_size, " [n] | Compute queue size n times (default: n == 1)"); add_cmd("show", do_show, " | Show queue contents"); add_param("length", &string_length, "Maximum length of displayed string", NULL); add_param("malloc", &fail_probability, "Malloc failure probability percent", NULL); add_param("fail", &fail_limit, "Number of times allow queue operations to return false", NULL); } ``` `console.c` 也提供了一些和 queue 無關的操作,如下: ```cpp void init_cmd() { cmd_list = NULL; param_list = NULL; err_cnt = 0; quit_flag = false; add_cmd("help", do_help_cmd, " | Show documentation"); add_cmd("option", do_option_cmd, " [name val] | Display or set options"); add_cmd("quit", do_quit_cmd, " | Exit program"); add_cmd("source", do_source_cmd, " file | Read commands from source file"); add_cmd("log", do_log_cmd, " file | Copy output to file"); add_cmd("time", do_time_cmd, " cmd arg ... | Time command execution"); add_cmd("#", do_comment_cmd, " ... | Display comment"); add_param("verbose", &verblevel, "Verbosity level", NULL); add_param("error", &err_limit, "Number of errors until exit", NULL); add_param("echo", &echo, "Do/don't echo commands", NULL); #if 0 add_param("megabytes", &mblimit, "Maximum megabytes allowed", NULL); add_param("seconds", &timelimit, "Maximum seconds allowed", change_timeout); #endif init_in(); init_time(&last_time); first_time = last_time; } ``` #### cmd 的執行 qtest 的 `main` 函式將基本的參數初始化後會呼叫 `run_console` 函式, `run_console` 函式用 while 迴圈包住 `cmd_select` 函式,直到使用者 quit 才會離開,`run_console` 函式如下: ```cpp bool run_console(char *infile_name) { if (!push_file(infile_name)) { report(1, "ERROR: Could not open source file '%s'", infile_name); return false; } while (!cmd_done()) { cmd_select(0, NULL, NULL, NULL, NULL); } return err_cnt == 0; } ``` `cmd_select` 會呼叫 `read_line` 函式將指令 parse 出來,並透過 `interpret_cmd` 函式在 cmd_list 中搜尋對應指令且執行,下面為搜尋指令過程的程式碼: ```cpp static bool interpret_cmda(int argc, char *argv[]) { if (argc == 0) return true; /* Try to find matching command */ cmd_ptr next_cmd = cmd_list; bool ok = true; while (next_cmd && strcmp(argv[0], next_cmd->name) != 0) next_cmd = next_cmd->next; if (next_cmd) { ok = next_cmd->operation(argc, argv); if (!ok) record_error(); } else { report(1, "Unknown command '%s'", argv[0]); record_error(); ok = false; } return ok; } ``` ###### tags: `進階電腦系統理論與實作 (Fall 2019)` ###### tags: `Linux 核心設計 2019`

    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