Publicité
Publicité

Contenu connexe

Publicité

Dernier(20)

Publicité

C語言結構與串列

  1. 結構與串列  定義結構  巢狀結構  結構陣列  結構指標  鏈結串列  使用鏈結串列實作堆疊  使用鏈結串列實作佇列  結構參數傳遞 Revised on July 28, 2021
  2. Make each day count  程式中需要處理筆資料時,我們會宣告⼀個變數;宣告變數時,還會 考量資料的性質,指定變數的資料型別。如果需要處理多筆相同型別 的資料時,就會宣告成陣列。至於要將多個彼此相關但資料型別不同 的變數集合在⼀起,就無法使用 C 語言的陣列處理  結構 (structures) 是 C 語言的延伸資料型態,屬於⼀種⾃訂資料型態 (user-defined types),可以讓程式設計者⾃行在程式碼定義新的資料 型態,將多個資料 (不限相同資料型態) 集合在⼀起 結構 2
  3. Make each day count  結構是由⼀或多個不同資料型態 (也可以是相同資料型態) 組成的集合, 並使用⼀個新名稱來代表新的資料型態,語法如下: struct 結構資料型別名稱 { 資料型別 欄位名稱1; 資料型別 欄位名稱2; … } 在結構內宣告的變數稱為該結構的成員 (members) 或欄位 (fields) struct student { char name[20]; int height; char tel[15]; };  student結構包含學生姓名 name、身高 height、和電話 tel 共3個欄位 定義結構 3
  4. Make each day count  定義了student結構後,在程式碼就可以使用struct關鍵字開頭加上結 構名稱來宣告結構變數 struct student std1;  宣告結構變數時,也可指定初始值 struct student std2 = {"王小明", 175, "1234567"};  結構變數的初始值必須使用大括號括起來,資料值使用「,」符號分隔,其 順序是對應結構欄位來指定各欄位的初值  編譯器在編譯時會將變數定址在偶數位的記憶體位址,因此,結構變數佔 用的記憶體空間大小,可能比結構各欄位的總和多幾個bytes 宣告結構變數 4 "王小明" 175 "1234567" 20 bytes 4 bytes 15 bytes m m+20 m+24 m+39
  5. Make each day count  在建立結構變數後,就可以使用「.」運算子存取結構的成員變數 std1.age = 176; strcpy(std1.tel, "7654321");  上述程式碼,因為第2個成員變數是字串,所以使用strcpy()函數指定變數 的字串內容 存取結構的成員變數 5
  6. Make each day count  當結構中的某個欄位成員是另⼀個結構變數,稱為巢狀結構 struct ename { char lastname[12]; char firstname[12]; }; struct person { char id[11]; char cname[10]; struct ename name; };  存取巢狀結構欄位資料時,同樣使用「.」運算子逐層存取結構的成員 變數 struct person p = {"Z123456789", "李四", {"Lee", "Sam"}}; printf("姓名: %sn", p.cname); printf("英文姓名: %s %sn", p.name.firstname, p.name.lastname); 巢狀結構 1/3 6
  7. Make each day count #include <stdio.h> #include <stdlib.h> struct date { int year; int month; int day; }; struct ename { char lastname[12]; char firstname[12]; }; void nested_struct(){ struct person { char id[11]; char cname[10]; struct date birthday; struct ename name; }; 巢狀結構 2/3 7
  8. Make each day count void nested_struct(){ struct person { char id[11]; char cname[10]; struct date birthday; struct ename name; }; struct person p = {"Z123456789", "李四", {2001, 2, 2}, {"Lee", "Sam"}}; printf("身份證字號: %sn", p.id); printf("姓名: %sn", p.cname); printf("生日: %d/%d/%dn", p.birthday.year - 1911, p.birthday.month, p.birthday.day); printf("英文姓名: %s %sn", p.name.firstname, p.name.lastname); } 巢狀結構 3/3 8
  9. Make each day count  結構陣列是指每個陣列元素裡所存放的都是同⼀種結構資料,例如下 列程式碼 numeric[] 就是由 char_code 結構組成之陣列 struct char_code { char ch; char ascii; }; struct char_code numeric[10]; 結構陣列 1/2 9
  10. Make each day count #include <stdio.h> #include <stdlib.h> struct char_code { char ch; char ascii; }; void struct_ary() { struct char_code numeric[10]; int i; for (i = 0; i < 10; i++){ numeric[i].ch = '0' + i; numeric[i].ascii = '0' + i; } for (i = 0; i < 10; i++){ printf("字元%c ASCII碼 = %dn", numeric[i].ch, numeric[i].ascii); } } 結構陣列 2/2 10
  11. Make each day count  如同 C 語言其他資料型態的指標,指標也可以指向結構,我們可以建 立指標來指向結構。例如:宣告 time 結構儲存時間資料,如下所示: struct time { int hours; int minutes; };  因為指標需要指向結構變數的位址,所以需要先宣告結構變數,然後 才能建立指向此結構變數位址的指標,如下所示: struct time now, *ptr;  上述程式碼宣告結構變數 now 和結構指標 ptr,接著將結構指標指向 結構,如下所示: ptr = &now; //結構指標 ptr 指向結構變數 now 的位址 結構指標 1/2 11
  12. Make each day count  現在,我們可以使用指標存取結構的成員變數,如下所示: (*ptr).minutes = 35;  上述程式碼使用取值運算子取得結構變數now後,再存取成員變數 minutes 的值,相當於執行程式碼 now.minutes = 35;  C 語言提供結構指標的「->」運算子,可以直接存取結構的成員變數 值,如下所示: ptr->hours = 18;  上述變數ptr是結構指標,可以存取成員變數hours的值。請注意!當 在 C 程式碼中看到「->」運算子時,就表示變數是指向結構的指標變 數 結構指標 2/2 12
  13. Make each day count  char *gets(char *str) 從標準輸入中讀取⼀行(直到換行字元或文件結束字元),並將其儲存到 由str指向的字串變數 char name[16]; gets(name);  int getche(void) 從標準輸入讀取⼀個字元,它不使用緩衝區來存儲輸入字元,讀取輸 入字元立即返回,而無需等待Enter鍵 char gender; gender = getche();  int getch(void) 功能類似getche(),但輸入的字元不會顯示螢幕上 字串及字元輸入函式 13
  14. Make each day count #include <stdio.h> #include <stdlib.h> #include <string.h> #define SIZE 3 struct student { char no[8]; char name[16]; char gender; }; void struct_ptr(){ struct student p[SIZE]; struct student *ptr; int i, flag = -1; char keyin[8]; Lab 使用資料結構 1/3 14
  15. Make each day count ptr = p; printf(" ===學籍登錄==="); for (i = 0; i < SIZE; i++){ printf("n第%2d 學生學號 = ", i + 1); gets((*ptr).no); printf("第%2d 學生姓名 = ", i + 1); gets(ptr->name); printf("第%2d 學生性別(F/M) = ", i + 1); ptr->gender = getche(); } ptr = p; printf(" ===學生資料查詢===n 學號 = "); gets(keyin); for (i = 0; i < SIZE; i++, ptr++){ if (strcmp(ptr->no, keyin) == 0) { flag = i; break; } } Lab 使用資料結構 2/3 15
  16. Make each day count if (flag == -1) printf("查無資料!n"); else { printf("學號 = %st", p[flag].no); printf("姓名 = %st", (*ptr).name); printf("性別 = %cn", ptr->gender); } } Lab 使用資料結構 3/3 16
  17. Make each day count  當結構中含有⼀個欄位指標能指到下⼀筆資料的位址,使得資料能構 ⼀筆接著⼀筆像鍊子⼀樣環環相扣,這種架構在資料結構中稱為鏈結 串列 (linked list) struct node { char data[12]; struct node *next; };  相較於陣列,鏈結串列中的每⼀筆記錄在記憶體中的位址是不連續, 雖然比結構陣列多出⼀個欄位,佔用較多記憶體,但在資料的插入與 刪除作業,比陣列方便快速 (陣列資料新增或刪除都需大量資料搬移) 鏈結串列 1/8 17 head next data next data next data next data next data NULL
  18. Make each day count  刪除串列資料  插入串列資料 鏈結串列 2/8 18 head next data next data next data next data next data NULL head next data next data next data next data next data NULL next data NEW Node
  19. Make each day count  建立鏈結串列  宣告 3 個結構指標 struct node *head, *ptr, *newptr;  產生第⼀個節點 (node1) newptr = (struct node*)malloc(sizeof(struct node));  設定 node1 節點的初值 strcpy(newptr->data, "node1"); newptr->next = NULL; 鏈結串列 3/8 19 newptr next data newptr next data NULL node1
  20. Make each day count  將 head 與 ptr 指標都指到 node1 節點 head = ptr = newptr;  產生第二個節點 (node2),並將newptr指向該節點起始位址 newptr = (struct node*)malloc(sizeof(struct node)); 鏈結串列 4/8 20 ptr next data NULL node1 newptr head newptr ptr next data NULL node1 head
  21. Make each day count  設定 node1 節點的初值 strcpy(newptr->data, "node2"); newptr->next = NULL;  將 node2 串接到 node1 之後 ptr->next = newptr; ptr = newptr; 鏈結串列 5/8 21 newptr ptr next data NULL node1 head NULL node2 node1 head newptr NULL node2 ptr
  22. Make each day count #include <stdio.h> #include <stdlib.h> #include <string.h> struct node { char data[12]; struct node *next; }; void linked_list(){ struct node *head, *ptr, *newptr; int i; //建立node1 newptr = (struct node*)malloc(sizeof(struct node)); strcpy(newptr->data, "node1"); newptr->next = NULL; head = ptr = newptr; 鏈結串列 6/8 22
  23. Make each day count //建立node2 newptr = (struct node*)malloc(sizeof(struct node)); strcpy(newptr->data, "node2"); newptr->next = NULL; ptr->next = newptr; ptr = newptr; //建立node3 newptr = (struct node*)malloc(sizeof(struct node)); strcpy(newptr->data, "node3"); newptr->next = NULL; ptr->next = newptr; ptr = newptr; 鏈結串列 7/8 23
  24. Make each day count //顯示所有資料 ptr = head; i = 1; while (ptr != NULL){ printf("n 第%d節點 ==> 資料: %sn", i++, ptr->data); ptr = ptr->next; } } 鏈結串列 8/8 24
  25. Make each day count  堆疊 (stack) 是⼀種特殊的儲存空間,只允許在有序的線性資料集合的 ⼀端 (稱為堆疊頂端 top) 進行加入資料 (push) 和移除資料 (pop) 運算。 因而按照後進先出 (LIFO, Last In First Out) 的原理運作 堆疊 25
  26. Make each day count #include <stdio.h> #include <stdlib.h> #include <conio.h> struct node { int content; struct node *next; }; struct node *top = NULL; void push(int data){ struct node *newnode; newnode = (struct node *)malloc(sizeof(struct node)); newnode->content = data; newnode->next = top; top = newnode; } 使用鏈結串列實作堆疊 1/4 26 top NULL next newnode data top NULL newnode data 執行newnode->next = top;後 執行top = newnode;後
  27. Make each day count int pop(void){ struct node *temp = top; int data; if (temp == NULL) return '0'; data = top->content; top = top->next; free(temp); return data; } void display(void){ struct node *temp = top; if (temp == NULL) { printf("n空堆疊"); return; } printf("n堆疊資料:"); while (temp != NULL) { printf(" %d,", temp->content); temp = temp->next; } } 使用鏈結串列實作堆疊 2/4 27 top temp NULL 執行top = top->next;後 top temp NULL 執行free(temp);後 top temp NULL NULL
  28. Make each day count void test_stack(){ int data; display(); push(1); push(2); push(3); display(); data = pop(); if (data == '0') printf("n空堆疊"); else printf("n從推疊取出資料 %d", data); display(); data = pop(); if (data == '0') printf("n空堆疊"); else printf("n從推疊取出資料 %d", data); data = pop(); if (data == '0') printf("n空堆疊"); else printf("n從推疊取出資料 %d", data); data = pop(); if (data == '0') printf("n空堆疊"); else printf("n從推疊取出資料 %d", data); } 使用鏈結串列實作堆疊 3/4 28
  29. Make each day count 使用鏈結串列實作堆疊 4/4 29
  30. Make each day count  佇列 (Queue) 是⼀種特殊的儲存空間,只允許在後端 (rear) 加入資料, 在前端 (front) 取出資料。因而按照先進先出 (FIFO, First-In-First-Out) 的的原理運作 佇列 30
  31. Make each day count #include <stdio.h> #include <stdlib.h> #include <conio.h> struct qnode { int content; struct qnode *next; }; struct qnode *front, *rear; void init_queue(void){ front = (struct qnode *)malloc(sizeof(struct qnode)); rear = front; rear->next = NULL; } 使用鏈結串列實作佇列 1/6 31 ? NULL front rear
  32. Make each day count void put_data(int data){ struct qnode *newnode; newnode = (struct qnode *)malloc(sizeof(struct qnode)); newnode->content = data; newnode->next = NULL; rear->next = newnode; rear = newnode; } 使用鏈結串列實作佇列 2/6 32 ? front rear 執行rear->next = newnode;後 data NULL newnode ? rear 執行rear = newnode;後 data NULL newnode front
  33. Make each day count int get_data(void){ struct qnode *temp; int data; if (front == rear) return '0'; temp = front->next; data = temp->content; free(front); front = temp; return data; } 使用鏈結串列實作佇列 3/6 33 ? front data temp data NULL rear 執行temp = front->next;後 front data temp data NULL rear 執行free(front);後 NULL front temp data NULL rear 執行front = temp;後
  34. Make each day count void show_queue(void){ struct qnode *temp; int data; if (front == rear) { printf("n空佇列"); return; } printf("n佇列資料:"); temp = front->next; while(temp != NULL) { printf(" %d,", temp->content); temp = temp->next; } } 使用鏈結串列實作佇列 4/6 34
  35. Make each day count void test_queue(){ int data; init_queue(); show_queue(); put_data(1); put_data(2); put_data(3); show_queue(); data = get_data(); if (data == '0’) printf("n空佇列"); else printf("n從佇列取出資料 %d", data); show_queue(); data = get_data(); if (data == '0’) printf("n空佇列"); else printf("n從佇列取出資料 %d", data); 使用鏈結串列實作佇列 5/6 35
  36. Make each day count data = get_data(); if (data == '0’) printf("n空佇列"); else printf("n從佇列取出資料 %d", data); data = get_data(); if (data == '0’) printf("n空佇列"); else printf("n從佇列取出資料 %d", data); } 使用鏈結串列實作佇列 6/6 36
  37. Make each day count  結構變數與⼀般變數⼀樣,當引數時可以使用傳址呼叫或傳址呼叫 #include <stdio.h> #include <stdlib.h> #include <math.h> struct scores { char no[8]; int score; }; void func(struct scores x){ x.score = sqrt(x.score) * 10; printf("傳值呼叫函式內n"); printf("學號%s 成績 = %dn", x.no, x.score); } 結構參數傳遞 1/6 37
  38. Make each day count void call_by_value() { struct scores john = {"08101", 40}; printf("傳值呼叫前n"); printf("學號%s 成績 = %dn", john.no, john.score); func(john); printf("傳值呼叫後n"); printf("學號%s 成績 = %dn", john.no, john.score); } 結構參數傳遞 2/6 38
  39. Make each day count #include <stdio.h> #include <stdlib.h> #include <math.h> struct scores { char no[8]; int score; }; void func_a(struct scores *x){ x->score = sqrt(x->score) * 10; printf("傳值呼叫函式內n"); printf("學號%s 成績 = %dn", x->no, x->score); } 結構參數傳遞 3/6 39
  40. Make each day count void call_by_address() { struct scores john = {"08101", 40}; printf("傳值呼叫前n"); printf("學號%s 成績 = %dn", john.no, john.score); func_a(&john); printf("傳值呼叫後n"); printf("學號%s 成績 = %dn", john.no, john.score); } 結構參數傳遞 4/6 40
  41. Make each day count  結構陣列當引數時,則是使用傳址呼叫 #include <stdio.h> #include <stdlib.h> struct student { char no[8]; char name[16]; int score; }; int search(int, struct student[]); void show(struct student[], int); void call_by_ary() { struct student s[] = {{"8101", "Tom", 80}, {"8102", "John", 88}, {"8103", "Kent", 95}}; int i; i = search(sizeof(s)/sizeof(struct student), s); show(s, i); } 結構參數傳遞 5/6 41
  42. Make each day count int search(int arysize, struct student sary[]){ int i, max, num; struct student *ptr = sary; max = num = 0; for (i = 0; i < arysize; i++, ptr++){ if (ptr->score > max){ max = ptr->score; num = i; } } return num; } void show(struct student sary[], int i){ printf("%st%st%d", sary[i].no, sary[i].name, sary[i].score); } 結構參數傳遞 6/6 42
  43. Make each day count  程式中宣告結構後,為了方便宣告 (不用再加上struct關鍵字),我們可 以使用typedef賦予別名,這個別名是⼀個新增的識別字,可以用來定 義全新的資料型態 struct item { char name[30]; double cost; int quantity; }; struct item phone;  我們可使用 typedef 為 item 結構定義⼀個新識別字的型態,之後再用 這個新識別字的型態宣告變數 typedef struct item inventory; inventory phone; 使用typedef建立C語言的新型態 43
Publicité