# 2020q3 Homework1 (lab0) {%hackmd QnyEFBdERZebn4iQDXNPnA %} contributed by < jeff14994 > ###### tags: `sysprog2020` `2020` `lab0` `hw1` `week1` ## 開發環境 ```bash= #OS: Linux 5.3.0-64-generic #58-Ubuntu SMP Fri Jul 10 19:33:51 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux #gcc version gcc (Ubuntu 9.2.1-9ubuntu2) 9.2.1 20191008 ``` ## 作業要求 1. q_new: 建立新的「空」佇列; 2. q_free: 釋放佇列所佔用的記憶體; 3. q_insert_head: 在佇列開頭 (head) 加入 (insert) 給定的新元素 (以 LIFO 準則); 4. q_insert_tail: 在佇列尾端 (tail) 加入 (insert) 給定的新元素 (以 FIFO 準則); 5. q_remove_head: 在佇列開頭 (head) 移除 (remove) 給定的元素。 6. q_size: 計算佇列中的元素數量。 7. q_reverse: 以反向順序重新排列鏈結串列,該函式不該配置或釋放任何鏈結串列元素,換言之,它只能重排已經存在的元素; 8. q_sort: 「進階電腦系統理論與實作」課程所新增,以遞增順序來排序鏈結串列的元素,可參閱 Linked List Sort 得知實作手法; ## 實作 ### queue.h 首先,先在 queue.h 加入`dlist_ele_t *tail`,使得 Queue 有 tail 的元素。 ```c= /* Queue structure */ typedef struct { list_ele_t *head; /* Linked list of elements */ list_ele_t *tail; int size; } queue_t; ``` ### queue.c 按題目要求,若是一個空的 Queue,則回傳 NULL。第9行,用 if 來判斷 q 是否為空的 Queue。 第12行,另外利用 q->tail 增加 tail 的元素,並將其設定指向 NULL,目的是指向 Queue 的尾端。 1. q_new: ```c= /* * Create empty queue. * Return NULL if could not allocate space. */ queue_t *q_new() { queue_t *q = malloc(sizeof(queue_t)); if (q == NULL) return NULL; q->head = NULL; q->tail = NULL; q->size = 0; return q; } ``` 若 Queue head 指標為空值,則無需檢查。若非空值,則遍歷整個 Queue 與其 linked list 將其刪除。q->head->next 將其指向下一個 link list 位址,若為空值則跳出迴圈 2. q_free: ```c= /* Free all storage used by queue */ void q_free(queue_t *q) { /* Free queue structure */ if (q == NULL) return; // if head exists, we keep on searching next while (q->head) { // Update tmp list_ele_t *tmp = q->head; q->head = q->head->next; // freeing the strings free(tmp->value); // freeing the list elements free(tmp); } free(q); } ``` queue.h 裡有描述代辦事項: 3. q_insert_head: - Attempt to insert element at tail of queue. - Return true if successful. - Return false if q is NULL or could not allocate space. - Argument s points to the string to be stored. - The function must explicitly allocate space and copy the string into it. 不像strcpy(),strncpy()不會向dest追加 '\0',這會引起了很多不合常理的問题。因此若使用strcpy(),length 要再減一,因為strcpy() 會自動增加 '\0' 在 make test 的時候跳出這個警告: ```c= Dangerous function detected in /home/jeff14994/Desktop/linux2020/week1/lab0-c/queue.c 76: strcpy(newh->value, s); Read 'Common vulnerabilities guide for C programmers' carefully. https://security.web.cern.ch/security/recommendations/en/codetools/c.shtml ``` 因為 strcpy 不會檢查邊界,因此可能產生 Buffer over flow,有心人士可以藉此控制 EIP 暫存器,執行任意程式。 ```c= bool q_insert_head(queue_t *q, char *s) { /* TODO: What should you do if the q is NULL? */ /* What if either call to malloc returns NULL? */ if (q == NULL) { return false; } // New head to be allocated memory list_ele_t *newh; newh = malloc(sizeof(list_ele_t)); if (newh == NULL) { return false; } /* Don't forget to allocate space for the string and copy it */ // Allocate space for string to checok int length = strlen(s) + 1; newh->value = malloc(sizeof(char) * (length)); if (newh->value == NULL) { free(newh); return false; } // Copy from the source to the newly allocated space strcpy(newh->value, s); newh->next = q->head; // Move head in front of newh q->head = newh; // If No any element if (q->tail == NULL) { q->tail = newh; } // Queue size grows, thus plus one q->size++; return true; } ``` 4. q_insert_tail: 與 q_insert_head 大同小異,差異在第30行的判斷,判斷 Queue 是否為空? 若是,則將新的 tail 加在 head 之後;若否,則將新的 tail 加在原本 tail 指標的下一個位址。透過第36行,將 q->tail 重新指向 link list 最後端。[備忘:忘記加了:tail = newt; 導致分數提不起來。] ```c= bool q_insert_tail(queue_t *q, char *s) { /* TODO: You need to write the complete code for this function */ /* Remember: It should operate in O(1) time */ if (q == NULL) { return false; } // New head to be allocated memory list_ele_t *newt; // newt represents new tail newt = malloc(sizeof(list_ele_t)); if (newt == NULL) { return false; } /* Don't forget to allocate space for the string and copy it */ // Allocate space for string to checok int length = strlen(s) + 1; newt->value = malloc(sizeof(char) * (length)); if (newt->value == NULL) { free(newt); return false; } // Copy from the source to the newly allocated space strncpy(newt->value, s, length); // Set this element to the last one newt->next = NULL; // See if the queue is empty or not. If yes, the head is the new tail, if // not, put the new tail in the next of the original tail if (q->size == 0) { q->head = newt; } else { q->tail->next = newt; } // Move q->tail to newt q->tail = newt; // Queue size grows, thus plus one q->size++; return true; } ``` 5. q_remove_head: 參考 dalaoqi 同學的做法,用 sprinrf() 複製字串。sprintf()用法: ```c= int snprintf(char *str, size_t size, const char * restrict format, ...) ``` 若 Queue 是 NULL 或空值, return false。若 sp 不等於 NULL ,則將字串複製到 sp。並且更新 q->head 到下一個位置、並釋放記憶體位置、並將 size 減一。 ```c= bool q_remove_head(queue_t *q, char *sp, size_t bufsize) { /* TODO: You need to fix up this code. */ /* TODO: Remove the above comment when you are about to implement. */ if (q == NULL || q->head == NULL) { return false; } list_ele_t *tmp = q->head; // If sp is not NULL copy the string to sp if (sp != NULL) { snprintf(sp, sizeof(bufsize), "%s", tmp->value); } // Update head to next node q->head = q->head->next; free(tmp->value); free(tmp); // Minus one in aize q->size--; return true; } ``` 6. q_size: 在 O(1)時間回傳queue的大小 ```c= int q_size(queue_t *q) { if (q == NULL || q->head == NULL) { return 0; } else { return q->size; } } ``` 7. q_reverse: 參考 ZhuMon 的畫的流程圖,以實現程式碼。我將註解寫在程式碼當中 ```c= void q_reverse(queue_t *q) { /* TODO: You need to write the code for this function */ /* TODO: Remove the above comment when you are about to implement. */ if (q == NULL || q->head == NULL) { return; } list_ele_t *curr = q->head; list_ele_t *prev = NULL; // See if q->head exists, if yes, than keep going while (q->head) { // get next node list_ele_t *next = q->head->next; // Change the head->next to previous node q->head->next = prev; // Update prev pointer prev = q->head; // Update q->head to next node q->head = next; } // Update head to tail q->head = q->tail; // Update tail to head q->tail = curr; } ``` 原本分數測出來 71/100,結果做完 reverse 後,下降到 65/100。原因待查。 8. q_sort: ## TODO - [ ] Valgrind - [ ] 論文 Dude, is my code constant time?