# 2019q1 Homework1 (lab0) contributed by < `njjack` > :::danger 注意看 [2019q1 Homework1 (作業區)](https://hackmd.io/s/SJ4kPZYS4) 的要求,HackMD 網址應採 publish 後的網址,請留意細節,及早變更。包含空白和排版,均有指定格式。 無論標題和內文中,==中文和英文字元之間要有空白字元== (對排版和文字搜尋有利) :notes: jserv ::: [C Programming Lab](http://www.cs.cmu.edu/~213/labs/cprogramminglab.pdf) ## 環境 ~~~ $ cat /etc/os-release NAME="Ubuntu" VERSION="16.04.3 LTS (Xenial Xerus)" ID=ubuntu ID_LIKE=debian PRETTY_NAME="Ubuntu 16.04.3 LTS" VERSION_ID="16.04" HOME_URL="http://www.ubuntu.com/" SUPPORT_URL="http://help.ubuntu.com/" BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/" VERSION_CODENAME=xenial UBUNTU_CODENAME=xenial ~~~ ~~~ $ lscpu Architecture: x86_64 CPU 作業模式: 32-bit, 64-bit Byte Order: Little Endian CPU(s): 4 On-line CPU(s) list: 0-3 每核心執行緒數:2 每通訊端核心數:2 Socket(s): 1 NUMA 節點: 1 供應商識別號: GenuineIntel CPU 家族: 6 型號: 61 Model name: Intel(R) Core(TM) i5-5200U CPU @ 2.20GHz 製程: 4 CPU MHz: 1609.716 CPU max MHz: 2700.0000 CPU min MHz: 500.0000 BogoMIPS: 4389.82 虛擬: VT-x L1d 快取: 32K L1i 快取: 32K L2 快取: 256K L3 快取: 3072K NUMA node0 CPU(s): 0-3 ~~~ ## 題目 實作 queue functions * q_new * q_free * q_insert_head * q_insert_tail * q_remove_head * q_size * q_reverse ## 實作 考慮題目要求 q_insert_tail( ) 和 q_size( ) 執行時間需為 $O(1)$,在 queue_t 結構中加入 `size` 和 `tail` ```clike= typedef struct { list_ele_t *head; /* Linked list of elements */ list_ele_t *tail; int size; } queue_t; ``` ### q_new ```clike= queue_t *q_new() { queue_t *q = malloc(sizeof(queue_t)); /* What if malloc returned NULL? */ if (!q) { return NULL; } q->head = NULL; q->tail = NULL; q->size = 0; return q; } ``` ### q_free ```clike= void q_free(queue_t *q) { /* How about freeing the list elements and the strings? */ /* Free queue structure */ if (q) { list_ele_t *temp; while (q->head) { temp = q->head; q->head = temp->next; free(temp); } free(q); } } ``` ### q_insert_head ```clike= bool q_insert_head(queue_t *q, char *s) { /* What should you do if the q is NULL? */ if (!q) { return false; } list_ele_t *newh; newh = malloc(sizeof(list_ele_t)); if (!newh) { return false; } /* Don't forget to allocate space for the string and copy it */ /* What if either call to malloc returns NULL? */ newh->value = strdup(s); if (!newh->value) { free(newh); return false; } newh->next = q->head; q->head = newh; if (q->size == 0) { q->tail = newh; } q->size++; return true; } ``` ### q_insert_tail ```clike= bool q_insert_tail(queue_t *q, char *s) { if (!q) { return false; } list_ele_t *newh; newh = malloc(sizeof(list_ele_t)); if (!newh) { return false; } newh->value = strdup(s); if (!newh->value) { free(newh); return false; } newh->next = NULL; if (q->size == 0) { q->head = newh; } else { q->tail->next = newh; } q->tail = newh; q->size++; return true; } ``` ### q_remove_head ```clike= bool q_remove_head(queue_t *q, char *sp, size_t bufsize) { if (q == NULL || q->size == 0) { return false; } list_ele_t *temp = q->head; if (sp) { memcpy(sp, temp->value, bufsize - 1); sp[bufsize - 1] = '\0'; } q->head = temp->next; free(temp); q->size--; return true; } ``` ### q_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 0; return q->size; } ``` ### q_reverse ```clike= void q_reverse(queue_t *q) { if (q == NULL || q->size == 0) { return; } list_ele_t *current = q->head; list_ele_t *cnext = current->next; list_ele_t *temp = current; while (cnext) { current = cnext; cnext = cnext->next; current->next = temp; temp = current; } q->tail = q->head; q->tail->next = NULL; q->head = current; } ``` ## 自動評分系統運作原理 ### command command 以 `struct CELE` 表示,儲存在 single linked list 中 ```clike= struct CELE { char *name; cmd_function operation; char *documentation; cmd_ptr next; }; ``` 呼叫 add_cmd 將一個新的 command 加入 linked list 中適當位置(按字母順序排序) ```clike= void add_cmd(char *name, cmd_function operation, char *documentation) { cmd_ptr next_cmd = cmd_list; cmd_ptr *last_loc = &cmd_list; while (next_cmd && strcmp(name, next_cmd->name) > 0) { last_loc = &next_cmd->next; next_cmd = next_cmd->next; } cmd_ptr ele = (cmd_ptr) malloc_or_fail(sizeof(cmd_ele), "add_cmd"); ele->name = name; ele->operation = operation; ele->documentation = documentation; ele->next = next_cmd; *last_loc = ele; } ``` 每次 command 輸入會呼叫 `interpret_cmda` 來搜尋 linked list ,找到並呼叫 command 對應之處理函式 (實際上每次 command 輸入按 function call 順序會呼叫 `cmd_select` , `interpret_cmd` , `parse_args`, 最後才將 parsing 後得到之 "command that has already been split into arguments" 作為 input 呼叫 `interpret_cmda` ) ```clike= /* Execute a command that has already been split into arguments */ 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; } ``` ### memory management * harness.h 中藉由 macro 將 `malloc` 和 `free` 替換成 `test_malloc` 和 `test_free` ```clike= #define malloc test_malloc #define free test_free ``` * 以 `block_ele_t` 結構實作一 doubly linked list ,記錄已分配之記憶體空間。 * 其中 `payload[0]` 為 [Array of Length Zero](https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html) 技巧之使用。每一段已分配記憶體空間都有一 `block_ele_t` 結構之 header ,藉由存取 payload 可直接存取此 header 之後的記憶體空間。 ```clike= typedef struct BELE { struct BELE *next; struct BELE *prev; size_t payload_size; size_t magic_header; /* Marker to see if block seems legitimate */ unsigned char payload[0]; /* Also place magic number at tail of every block */ } block_ele_t; ``` * 呼叫 `test_malloc` 實際會分配的記憶體空間,除了提出請求之記憶體大小,還包含 header(一個 `block_ele_t` 結構) 和 tail (一個 magic number) * `test_malloc` 成功配置記憶體空間後,會將 header 中 `magic_header` 設 `MAGICHEADER` , 並呼叫 `find_footer` 找到 tail 的 magic number 設為 `MAGICFOOTER` * `MAGICHEADER` 和 `MAGICFOOTER` 分別為常數 `0xdeadbeef` 和 `0xbeefdead` ,作為存取位置是否合法的判斷依據 ```clike= void *test_malloc(size_t size) { ... block_ele_t *new_block = malloc(size + sizeof(block_ele_t) + sizeof(size_t)); if (new_block == NULL) { report_event(MSG_FATAL, "Couldn't allocate any more memory"); error_occurred = true; } new_block->magic_header = MAGICHEADER; new_block->payload_size = size; *find_footer(new_block) = MAGICFOOTER; ... } ``` * `test_free` 會檢查 tail 的 magic number 是否為 `MAGICFOOTER` ,若否,則釋放此空間非合法動作 * `test_free` 將`magic_header` 和 tail 的 magic number 都設為 `MAGICFREE` ```clike= void test_free(void *p) { ... block_ele_t *b = find_header(p); size_t footer = *find_footer(b); if (footer != MAGICFOOTER) { report_event(MSG_ERROR, "Corruption detected in block with address %p when " "attempting to free it", p); error_occurred = true; } b->magic_header = MAGICFREE; *find_footer(b) = MAGICFREE; ... } ``` * `test_malloc` 和 `test_free` 都會將配置或是放的記憶體空間初始為常數 `FILLCHAR` 已避免若某位置存放數值恰為前述已使用過之常數,可能造成判斷合法與否時誤判 `