# 2019q1 Homework1 (lab0) contributed by < `Laurora` > * [C Programming Lab](http://www.cs.cmu.edu/~213/labs/cprogramminglab.pdf) * [F01: lab0](https://hackmd.io/s/BJA8EgFB4#%E4%BD%9C%E6%A5%AD%E8%A6%81%E6%B1%82) :::danger 認真看作業要求,除了提及如何逐步達到要求,還需要進行: 1. 改善程式效能 2. 解釋自動評分系統運作的原理 3. 提及 qtest 的行為和裡頭的技巧 還未達成符合預期目標,請繼續付出! :notes: jserv ::: ## 作業要求 ### 透過 link list 實做 LIFO 與 FIFO queue * q new: 新增一個空的 queue * q free: 將 queue 清空 * q insert head: 從 queue 的 head 插入新的元素 ( 實做 LIFO ) * q insert tail: 從 queue 的 tail 插入新的元素 ( 實做 FIFO ) * q remove head: 從 queue 的 head 移除元素 * q size: 紀錄 queue 中有幾個元素 * q reverse: 反轉 queue 中所有元素的順序,不得另外配置元素空間協助反轉,過程必須直接針對原來存在的元素執行。 **注意** * q_insert_tail 和 q_size 的時間複雜度須為 $O(1)$ * 須透過呼叫 malloc() 針對各個 string 的長度分配相應的空間,使用完該空間以後要記得 free() ## 實作過程 ### queue.h ```clike typedef struct { list_ele_t *head, *tail; size_t qsize; } queue_t; ``` * 額外新增 `qsize` 即時紀錄 queue 空間大小,讓 q_size 可在 $O(1)$ 時間內完成,不須每次呼叫 q_size 便從 queue 的第一個元素開始數,此時所需時間為 $O(n)$ 。 * 同理,新增 `*tail` 紀錄 queue 的 tail element 使 q_insert_tail 可在 $O(1)$ 時間內完成。 ### q_new ```clike queue_t *q_new() { queue_t *q = malloc(sizeof(queue_t)); if (q == NULL) return NULL; else { q->head = q->tail = NULL; q->qsize = 0; } return q; } ``` * 配置新增的 queue 空間 * 對新增的 queue 做 initialization ### q_free :::danger 程式碼縮排和風格已有規範,請確保 HackMD 頁面所有程式碼列表都符合 :notes: jserv ::: ```clike void q_free(queue_t *q) { if (q == NULL) return; while (q->head) { list_ele_t *temp = q->head->next; free(q->head); q->head = temp; } free(q); } ``` * 清空整個 queue * 若 queue = NULL ,則跳出函式,避免 `dangling pointers` ### q_insert_head ```clike bool q_insert_head(queue_t *q, char *s) { if (!q) return false; list_ele_t *newh = NULL; newh = malloc(sizeof(list_ele_t)); if (!newh) { free(newh); return false; } if (q->qsize == 0) { newh->next = NULL; q->tail = newh; } newh->value = malloc(strlen(s) + 1); if (!newh->value) { free(newh->value); newh = NULL; return false; } strcpy(newh->value, s); newh->next = q->head; q->head = newh; q->qsize++; free(newh->value); newh = NULL; return true; } ``` * 在 queue 的 head 插入新元素 * 要記得 allocate 新的空間給 `newnode ( newh )` 以及新增 queue 的元素空間( `strlen(s)+1` ) * 新增元素之後要把暫存 newnode 的空間釋出( `free(new->value)` ) ### q_insert_tail ```clike bool q_insert_tail(queue_t *q, char *s) { if (!q) return false; list_ele_t *newt = NULL; newt = malloc(sizeof(list_ele_t)); if (newt == NULL) { free(newt); return false; } newt->value = malloc(strlen(s) + 1); // define newt if (newt->value) strcpy(newt->value, s); newt->next = NULL; if (q->qsize == 0) q->head = newt; //考慮 queue 原來為空 q->tail->next = newt; q->tail = newt; q->qsize++; free(newt->value); newt = NULL; return true; } ``` * 在 queue 的 tail 插入新元素 ### q_remove_head ```clike bool q_remove_head(queue_t *q, char *sp, size_t bufsize) { if (!q || q->qsize == 0) { return false; } if (q->qsize == 1) { q->qsize--; q->head = q->tail = NULL; q->head->value = NULL; return true; } list_ele_t *temp = q->head; q->head = q->head->next; if (sp) { memset(sp, '\0', bufsize);//將 sp 空間初始化 strncpy(sp, temp->value, bufsize - 1); } q->qsize--; return true; } ``` * 須考慮 queue 為空,避免 `dangling pointers` * strncpy 可指定 copy 的字串長度,由於 remove 使得需要 copy 的字串少一個,因此在這裡使用 `strncpy` 而非 `strcpy` ### q_size ```clike int q_size(queue_t *q) { if (!q) return 0; else return q->qsize; } ``` * 回傳 queue 空間大小 ### q_reverse ```clike void q_reverse(queue_t *q) { list_ele_t *next, *prev, *current; current = NULL; prev = NULL; if (q->qsize == 0) return; current = q->head; q->tail = current; // tail turns out to be head while (current != NULL) { next = current->next; current->next = prev; prev = current; current = next; q->head = prev; } } ``` * 將 queue 中的所有元素反轉 ### 實作過程整理與總結 * 過程中犯了最大的一個錯誤就是未考慮 queue 為空的條件,平常很少接觸指標的寫法,因此在分配指標之前經常忽略這個條件,到 `./qtest` 時才發現此盲點。 * `strdup()` 的轉換 + strdup() 可將字串複製到指定位址,然而並非標準 C 語言函式,是由 `malloc()` 和 `strcpy()` 兩個指令實作而來,最後使用完指定位址的 pointer 空間要記得釋放。常見轉換方式如下: ```clike char *d = malloc(strlen(s) + 1); strcpy(d, s); /* some instructions here */ free(d); ``` ## 解釋自動評分系統運作的原理 待補 ## qtest 的行為與技巧 待補