# 2020q3 Homework1 (lab0) contributed by < `ptzling310` > ## Struct at queue.h ### list_ele_t 定義一個 structure: list_ele_t 為 Link-list 含有 1. vlaue: 指向某個 char 的 pointer 2. next: 指向某個 struct ELE(list_ele_t) 的 pointer ```c= typedef struct ELE { char *value; struct ELE *next; } list_ele_t; ``` ```graphviz digraph structs { node [shape=record]; struct1 [label="{ list_ele_t |{ <value>value | <next>next }}"]; NULL1[label="...",shape=plaintext] NULL2[label="...",shape=plaintext] struct1:value -> NULL1 struct1:next -> NULL2 } ``` ### queue_t Queue的結構 含有 1. head: 指向某個 list_ele_t 的 pointer, 為 queue 之第一個 list 2. tail: 指向某個 list_ele_t 的 pointer, 為 queue 之最後一個 list 3. size: 紀錄 queue 有的 list 數量 ```c= typedef struct { list_ele_t *head; list_ele_t *tail; int size; } queue_t; ``` ```graphviz digraph structs { node [shape=record]; struct3 [label="{ queue_t |{ <head>value | size | <tail>tail }}"]; NULL1[label="...",shape=plaintext] NULL2[label="...",shape=plaintext] struct3:head -> NULL1 struct3:tail -> NULL2 } ``` ## Function at queue.c ### *q_new() 去建立一個 empty 的 queue 若有空間 --> 回傳位址 無空間 --> 回傳 NULL ```c= queue_t *q_new() { //配置一個空間,該空間 address 紀錄在 q queue_t *q = malloc(sizeof(queue_t)); //無法配置空間 if(!q){ return NULL; } //設定該 queue 之資料 q->head = NULL; q->tail = NULL; q->size = 0; return q; } ``` ### q_insert_head 在queue的尾插入一個char 插入成功-->回傳true 插入失敗--> 表示queue為NULL,回傳flase ```c= bool q_insert_head(queue_t *q, char *s) { //queue:NULL if(!q) return false; //queue: 有東西 //*newh: 指向新增的 list_ele_t list_ele_t *newh; newh = malloc(sizeof(list_ele_t)); //確定 malloc 成功 if(!newh) return false; //把資料放入list newh->next = NULL; //s: 存要希望存入的 value 的位址,造再給這些 char 一個空間,再連到存到 value //而為了儲存長度 l 的字串,需要至少 l + 1 個 char 元素,並在最後設定為 \0 字元 newh->value = malloc(sizeof(char) * (strlen(s) + 1)); if(!newh->value){ free(newh); return false; } memset(newh->value,'\0',strlen(s)+1); strncpy(newh->value,s,strlen(s)); //把list放入queue的head //設定head newh->next = q->head; q->head = newh; //若插入的是第一個list,則設定tail if(!q->tail){ q->tail = newh; q->head = newh; } else{ q->tail->next=newh; q->tail=newh; } // 更新q->size q->size+=1; return true; } ``` ### q_insert_tail 在 queue 的尾插入一個 lsit 插入成功-->回傳 true 插入失敗--> 表示 queue 為 NULL, 回傳 flase ```c= bool q_insert_tail(queue_t *q, char *s) { if(!q) return false; //新增一個 list list_ele_t *newh; newh = malloc(sizeof(list_ele_t)); if(!newh) return false; //設定 struct 內所需之值 newh->next=NULL; newh->value = malloc(sizeof(char)*strlen(s)+1); if(!newh->vlaue){ free(newh); return false; } memset(newh->value,'\0',strlen(s)+1); strncpy(newh->value,s,strlen(s)); //insert if(!q->tail){ q->head = newh; q->tail = newh; } else{ q->tail->next = newh; q->tail = newh; } q->size +=1; return true; } ``` ### q_remove_head 在佇列開頭 (head) 移除 (remove) 給定的元素。 移除成功-->回傳true 移除失敗-->表示 queue 為 NULL 或 empty ,回傳 flase ```c= bool q_remove_head(queue_t *q, char *sp, size_t bufsize) { //不用 remove head 的情況 if(!q || !q->head) return false; list_ele_t *tmp; //要記得free tmp = q->head; q->head = q->head->next; if(sp!=NULL){ memset(sp,0,bufsize); if(tmp->value){ strncpy(sp,tmp->value,bufsize-1); } } free(tmp->value); free(tmp); q->size -= 1; return true; } ``` ### list_ele_t *merge(list_ele_t *l1, list_ele_t *l2) ```c= list_ele_t *merge(list_ele_t *l1, list_ele_t *l2) { //do not need to merge if(!l1) return l2; if(!l2) return l1; //head指向依 NULL list_ele_t *head = NULL; //tmp指向一 pointer: head, 並且記錄 head 的 addre list_ele_t **tmp = &head; while(l1 && l2){ if(strcmp(l1->value,l2->value) < 0){ //l1<l2 //將l1指派給tmp,所以tmp會存l1的位址 *tmp = l1; l1 = l1->next; } else{ *tmp* = l2; l2 = l2->next; } tmp = &((*tmp)->next); } if(l1) *tmp = l1; if(l2) *tmp =l2; return head; } ``` ### list_ele_t *merge_sort(list_ele_t *head) ```c= list_ele_t *merge_sor(list_ele_t *head) { //do not need to sort if(!head || !head->next){ return head; } //split list_ele_t *h1 = head; list_ele_t *h2 = head->next; while(h2 && h2->next){ h1 = h1->next; h2 = h2->next->next; } h2 = h1->next; h1->next = NULL; h1 = head; //sort each list lsit_ele_t *list1 = merge_sort(h1); list_ele_t *list2 = merge_sort(h2); //merge two list into one return merge(list1,list2); } ``` ### void q_sort(queue_t *q*) ```c= void q_sort(queue_t *q*) { //q不存在或沒有指向的list, 則不用排序 if(!q || !q->head){ return; } //把queue所指的list丟去排序 q->head = merge_sort(q->head); //排序完成,要再更新q->tail while(q->tail->next){ q->tail=q->tail->next; } } ``` :::danger 思考: merge_sort 的成本高,如果 linked list 的 element 若數量不多,是不是就不要採用 merge_sort ::: ## 語法 ### strcmp() - 目的: 在 `q_sort()` 中,要求list依遞增方式排序, 又所存之值為string, 而非int, 所以須利用此語法達成字串排序 - 說明: ```c int strcmp(const char *str1, const char *str2) ``` str1: 指向第一個要比較的字串 str2: 指向第二個要比較的字串 - 字串的大小 依照 ASCII 來比較大小 - 返回值 str1 > str2 : return value > 0 str1 = str2 : return value = 0 str1 < str2 : return value < 0 - 舉例 ```c= #include <stdio.h> #include <string.h> int main () { char str1[15]; char str2[15]; int return_value; strcpy(str1, "abcdef"); strcpy(str2, "ABCDEF"); return_value = strcmp(str1, str2); if(return_value > 0) { printf("str1 is less than str2"); } else if(return_value < 0) { printf("str2 is less than str1"); } else { printf("str1 is equal to str2"); } return(0); } ``` 結果 ``` str1 is less than str2 ``` - 參考來源 [strcmp() - C語言庫函數](http://tw.gitbook.net/c_standard_library/c_function_strcmp.html) ### strcpy()、strncpy():字串複製 - 目的:為了在 insert 以及 remove 中處理要插入(或移除)的字串,需要先配置額外空間,將字串複製到新創造出的空間上。 - 說明: ```c char *strcpy( char *dest, const char *src ); char *strncpy( char *dest, const char *src, size_t count ); ``` src:字串來源 dest:將來源自串複製到此 - 舉例_strcpy() ```c= #include <stdio.h> #include <string.h> int main() { char src[40]; char dest[100]; //先把dest先都填入'\0' memset(dest, '\0', sizeof(dest)); //將"This is gitbook.net"複製到src strcpy(src, "This is gitbook.net"); //將src複製到dest strcpy(dest, src); printf("Final copied string : %s", dest); return(0); } ``` 結果 ``` Final copied string : This is gitbook.net ``` - 舉例_strncpy() ```c= char src[40]; char dest[12]; //把dest 都先填入\0 memset(dest, '\0', sizeof(dest)); //將字串複製到src內 strcpy(src, "This is tutorialspoint.com"); //複製src的前10個字元到dest strncpy(dest, src, 10); printf("Final copied string : %s\n", dest); ``` 結果 ``` Final copied string : This is tu ``` - 討論 1. 使用 `strcpy()` ,是依據 `/0` 作為結束的判斷,並且 `/0` 也會被複製。 2. 使用 `strcpy()` 可能會有 buffer overflow的問題,也就是 scr 複製到 dest 的長度太長。所以 dest 會占用到別的變數的記憶體空間、回傳 Segmentation Violation。 所以利用 `strncpy()` 來控制要複製幾個字元。 3. 若 `len(src) < n` (例:8 < 10),則會把不足的地方補上 `/0` ;若`len(src) > n` 則==不會==補 `/0` ,必須自己補上! 所以大多都再 `strncpy()` 前先把 dest 的值全部填 `/0` 。 ### 問題 1. strcpy()與```'\0'```的關係 2. 每次 alloc 一個 struct 後,都對結構內所有資料設值(容易忘記把 next 設 NULL --> 錯誤迴圈) 3. 字串比大小 ### 時間表 用來記錄做事進度 - [x] 2020/9/13:完成q_new, q_free_, q_size - [x] 2020/9/14:完成 q_insert_head, q_insert_tail, q_remove_head, q_reverse - [x] 2020/9/16:完成q_sort~~並修改註解~~ - [x] 2020/9/17:完成 lab0 中所用之語法、程式註解 - [ ] 2020/9:Valgrind記憶體分析 - [ ] 2020/9:Dude, is my code constant time? :::danger 因為之前沒有看懂 github 上對於 commit 的要求,所以變成再完成作業後才一次將 code 更新到github 上... ::: :::info (☍﹏⁰。) 覺得這個自己寫的不太好,之後補上詳細解釋,以免自己以後回來看某些地方又看不懂 ::: :::info commit 寫錯的話 $ git commit --amend commit 中要告知 function 時的用途、連帶影響的 function 也可以加上 TODO ::: ###### tags: `sysprog2020`