Presentation is loading. Please wait.

Presentation is loading. Please wait.

struct 可以在同一個名稱下擁有多種資料型態。使用struct能讓資料的存取和處理更為靈活。

Similar presentations


Presentation on theme: "struct 可以在同一個名稱下擁有多種資料型態。使用struct能讓資料的存取和處理更為靈活。"— Presentation transcript:

1 struct 可以在同一個名稱下擁有多種資料型態。使用struct能讓資料的存取和處理更為靈活。
Chap 15 struct與資料結構 struct 可以在同一個名稱下擁有多種資料型態。使用struct能讓資料的存取和處理更為靈活。

2 struct與資料結構 15.1 struct的宣告和使用 15.2 由struct構成的陣列 15.3 struct資料型態與函數參數的傳遞 15.4 struct實例的動態宣告 15.5 指標成員與資料結構 15.6 union資料型態 15.7 enum資料型態

3 struct的宣告和使用 組成份子稱為成員 (member) 或資料欄位 (data field)。 成員可以是各種不同的資料型態 (複合式資料型態)。 關鍵字struct是英文 structure (結構) 的縮寫,此種資料結構又稱為記錄(record)。

4 struct資料型態的宣告範例 Employee包括的資料成員有Name (姓名)、Phone (電話號碼) 以及Id (編號) 三種:
{ char Name[20]; char Phone[10]; int Id; }; // 注意這要用到「;」!

5 Struct 在記憶體中的儲存方式 各成員的儲存位置是連續的:

6 定義了兩個名稱分別為Ea和Eb的Employee變數。 由某一資料型態定義的變數稱為該資料型態的實例 (instance)。
使用struct 資料型態定義變數 可以使用標準的定義敘述。例如: Employee Ea, Eb; 定義了兩個名稱分別為Ea和Eb的Employee變數。 由某一資料型態定義的變數稱為該資料型態的實例 (instance)。

7 定義變數時一併給予初始值 例如,上面的敘述可進一步寫成:
Employee Ea = {"Ann", " ", 105}; Employee Eb = {"Joanne", " ", 106};

8 要存取Employee變數的個別資料欄位
必需同時給定變數名稱和資料成員名稱,中間用一個成員運算符號 (member operator)「.」隔開。例如: Ea.Name // 其值目前分別為 “Ann” Eb.Phone // 其值目前分別為“ ” Ea.Id // 其值目前分別為105 分別用來代表Ea這個Employee變數的三個成員,其值目前分別為“Ann”,“ ”和105。這個語法基本上和我們在10.1節介紹的成員函數的語法是一致的。

9 範例程式TestStruct.cpp 如何使用struct宣告自訂的資料型態,以及各欄位內的資料如何存取。

10 範例程式 檔案 TestStruct.cpp // TestStruct.cpp #include <iostream>
using namespace std; struct Employee { char Name[20]; char Phone[10]; int Id; }; // 主程式

11 int main() { EmployeeEa= {"Ann", " ", 105}; Employee Eb = {"Joanne", " ", 106}; cout << "Ea 的資料是:\n" << "姓名 : " << Ea.Name << '\n' << "電話號碼: " << Ea.Phone << '\n' << "編號 : " << Ea.Id << endl; cout << "Eb 的資料是:\n" << "姓名 : " << Eb.Name << '\n' << "電話號碼: " << Eb.Phone << '\n' << "編號 : " << Eb.Id << endl; return 0; }

12 執行結果 Ea 的資料是: 姓名 : Ann 電話號碼: 02384125 編號 : 105 Eb 的資料是: 姓名 : Joanne
編號 : 105 Eb 的資料是: 姓名 : Joanne 電話號碼: 編號 : 106

13 合併struct資料型態的宣告和變數的定義
例如: struct { char Name[20]; char Phone[10]; int Id; } Ea, Eb; 由於粗體字的部份本身就是已經是新定義的資料型態之具體內容,不用再取個名稱來代表它。

14 比較資料型態變數的語法

15 由struct構成的陣列 Employee Officer[50];
結合陣列和struct,可以一次完成很多具有相同結構的struct變數的定義。 例如,可以使用 Employee Officer[50]; 同時定義從Officer[0] 到Officer[49],共50個Employee變數

16 struct陣列各欄位的資料 cout << Officer[8].Name << endl;
cout << Officer[12].Phone << endl; cout << Officer[40].Id << endl;

17 範例程式StructArray.cpp 允許使用者逐一輸入各陣列元素的各成員值 (每輸入一個項目後,要按兩次Enter鍵)。

18 範例程式 檔案 StructArray.cpp
#include <iostream> using namespace std; const int NameSize = 20; const int PhoneSize = 10; struct Employee { char Name[NameSize]; char Phone[PhoneSize]; }; int main() const int Size = 2; Employee Officer[Size]; cout << "共 " << Size << " 個 Officers:\n";

19 for (int i=0; i<Size; i++)
{ cout << "請輸入 Officer[" << I << "] 的姓名: "; cin.getline(Officer[i].Name, NameSize, '\n'); cout << "電話號碼: "; cin.getline(Officer[i].Phone, PhoneSize, '\n'); } cout << "Officer[" << i << "] 的資料是:\n" << "姓名 : " << Officer[i].Name << '\n' << "電話號碼: " << Officer[i].Phone << '\n'; return 0;

20 執行結果 共2個Officers: 請輸入Officer[0] 的姓名: Alan John 電話號碼: 03-4521234
請輸入 Officer[1] 的姓名: Peter Pan 電話號碼: Officer[0] 的資料是: 姓名 : Alan John 電話號碼: Officer[1] 的資料是: 姓名 : Peter Pan

21 struct資料型態與函數參數的傳遞 由struct所定義的實例被用來做為參數傳遞時,其預設的語意是傳值 (pass-by-value)。
也就是說,在「被呼叫函數」內部將另外產生一個複製資料,而不會影響「呼叫函數」內的資料。

22 用struct所定義的實例來傳遞參數 例如,呼叫敘述可以寫成: 而被呼叫函數則可以定義成:
ShowMember(Ea); 而被呼叫函數則可以定義成: void ShowMember(Employee A) { cout << "資料的詳細內容是:\n" << "姓名 : " << A.Name << '\n' << "電話號碼: " << A.Phone << '\n' << "編號 : " << A.Id << endl; return; }

23 使用傳參照 (pass by reference) 改變struct實例的內容
例如: ChangeName(Ea, “Jackson”); 對應的「被呼叫函數」則定義成: void ChangeName (Employee& A, char NewName[]) { strcpy(A.Name, NewName); return; }

24 範例程式 檔案 StructFnc.cpp // StructFnc.cpp #include <iostream>
using namespace std; struct Employee { char Name[20]; char Phone[10]; int Id; }; void ShowMember(Employee A) cout << "資料的詳細內容是:\n" << "姓名 : " << A.Name << '\n' << "電話號碼: " << A.Phone << '\n' << "編號 : " << A.Id << endl; return; }

25 void ChangeName (Employee& A, char NewName[])
{ strcpy(A.Name, NewName); return; } // ============= 主程式 ======================== int main() { Employee Ea = {"Ann", " ", 105}; Employee Eb = {"Joanne", " ", 106}; ShowMember(Ea); ShowMember(Eb); ChangeName(Ea, "Jackson"); cout << "執行 ChangeName() 後:\n"; return 0; }

26 執行結果 資料的詳細內容是: 姓名 : Ann 電話號碼: 02384125 編號 : 105 姓名 : Joanne
編號 : 105 姓名 : Joanne 電話號碼: 編號 : 106 執行 ChangeName() 後: 姓名 : Jackson

27 使用指標改變struct實例的內容 使用傳址 (pass-by-address) 來達到使用參照的目的: 「被呼叫函數」則定義為
ChangeId(&Ea, ); 「被呼叫函數」則定義為 void ChangeId(Employee* pE, int NewId) { (*pE).Id=NewId; return; }

28 pE與Ea之間的關係

29 C++ 的具象指標符號 -> 將 (*pE).Name寫成: pE->Name
表示「由pE指向的struct變數內的成員 Name」。

30 進一步改寫 ChangeId() void ChangeId(Employee* pE, int NewId) {
pE->Id = NewId; return; }

31 範例程式 檔案 StructFnc2.cpp // StructFnc2.cpp #include <iostream>
using namespace std; struct Employee { char Name[20]; char Phone[10]; int Id; }; void ShowMember(Employee A) cout << "資料的詳細內容是:\n" << "姓名 : " << A.Name << '\n' << "電話號碼: " << A.Phone << '\n' << "編號 : " << A.Id << endl; return; }

32 void ChangeName (Employee& A, char NewName[])
{ strcpy(A.Name, NewName); return; } void ChangeId(Employee* pE, int NewId) { pE->Id = NewId; return;} // ========= 主程式 ======================== int main() { Employee Ea = {"Ann", " ", 105}; Employee Eb = {"Joanne", " ", 106}; ShowMember(Ea); ShowMember(Eb); ChangeId(&Ea, 208); cout << "執行 ChangeId() 後:\n"; return 0; }

33 執行結果 資料的詳細內容是: 姓名 : Ann 電話號碼: 02384125 編號 : 105 姓名 : Joanne
編號 : 105 姓名 : Joanne 電話號碼: 編號 : 106 執行 ChangeId() 後: 編號 : 208

34 亦即struct實例的動態記憶體配置(dynamic memory allocation)。 下列敘述則可以在執行時才臨時決定陣列的大小:
int Size; cin >> Size; Employee* pE = new Employee[Size];

35 struct實例的動態記憶體配置和回收 執行後會依指定的大小在記憶體的特殊區域,稱為記憶堆 (heap) 的地方,規劃出需要的記憶空間,並把第一個變數的開頭位址存入指標內。 如果此陣列不再需要,可以執行下列的敘述回收記憶體空間: delete [] pE;

36 例如,要取用第k個陣列元素內的成員Id,下述語法都是正確的:
使用陣列下標或指標算數存取內部成員 例如,要取用第k個陣列元素內的成員Id,下述語法都是正確的: Labor[k].Id (*(Labor + k)).Id (Labor + k)->Id pE[k].Id (*(pE + k)).Id (pE + k)->Id

37 範例程式DynStruct.cpp 示範動態產生由struct實例所構成的陣列,稱為Employee,之完整語法,並在事後回收記憶空間。

38 範例程式 檔案 DynStruct.cpp // DynStruct.cpp #include <iostream>
using std::cin; using std::cout; struct Employee { char Name[20]; char Phone[10]; int Id; }; // 主程式

39 int main() { int Size; cout << "請輸入 Employee 的數目:\n"; cin >> Size; Employee* pE = new Employee[Size]; delete [] pE; return 0; }

40 指標成員與資料結構 struct所宣告的資料型態可以使用「指標」做為成員。
指標成員可以指向自己所在的struct資料型態,稱為「自我參照」(auto-reference)。例如: Struct Data { int Id; Data* pD; };

41 串列 (lists) 定義一串的Data變數: 圖示如下:
Data D1, D2, D3; D1.pD = &D2; D2.pD = &D3; 圖示如下: 串列最後一個元素內的指標值為NULL (亦即 ‘\0’),用來做為檢查串列是否「到此為止」的根據。

42 串列 (lists) 「節點」(node): 每一個用來儲存資料的元素第一個節點稱為 「開頭」(head): 第一個節點。 「結尾」(tail): 最後的節點。

43 在陣列插入一個元素 必需同時將V[1] 及其之後的所有元素往右移,且無法應付陣列因長度增加而記憶空間可能不足的問題。
int* V = new int [Size]; 必需同時將V[1] 及其之後的所有元素往右移,且無法應付陣列因長度增加而記憶空間可能不足的問題。

44 「串列」可以帶來的便利 使用串列 (linked list) 可以較有效率地完成元素增刪的動作。 設想原先有A, B, C 三個元素串接在一起形成一個串列:

45 假設使用struct宣告了一個名叫 Element 的自訂資料形態:
一個串列的範例 假設使用struct宣告了一個名叫 Element 的自訂資料形態: struct Element { int Value; Element* Next; };

46 動態產生任意數目的Element (各實例的值在此為0, 2, 4, 6, …):
cout << "請輸入 Element 的數目:\n"; cin >> Size; Element* pE = new Element[Size]; for (int i=0; i<(Size-1); i++) pE[i].Next = pE + i +1; pE[Size-1].Next = NULL; for (int i=0; i<(Size); i++) pE[i].Value = i*2;

47 顯示現有串列元素 不斷更換指標使它指向下一個元素的位址。 Element* pShow;
for (pShow = pE; pShow != NULL; pShow=pShow->Next) cout << pShow->Value << ' '; 不斷更換指標使它指向下一個元素的位址。

48 以while迴圈顯示現有串列元素 Element* pShow=pE; while (pShow != NULL) {
cout << pShow->Value << ' '; pShow = pShow->Next; }

49 將顯示串列內容的功能封裝到函數中 void ShowElement(Element* pShow) {
while (pShow != NULL) cout << pShow->Value << ' '; pShow = pShow->Next; }

50 範例程式 檔案 ListStruct.cpp // ListStruct.cpp #include <iostream>
using namespace std; struct Element { int Value; Element* Next; }; void ShowElement(Element* pShow) while (pShow != NULL) cout << pShow->Value << ' '; pShow = pShow->Next; }

51 // ---主程式------------------------
int main() { int Size; cout << "請輸入 Element 的數目:\n"; cin >> Size; Element* pE = new Element[Size]; for (int i=0; i<(Size-1); i++) pE[i].Next = pE + i +1; pE[Size-1].Next = NULL; for (int i=0; i<(Size); i++) pE[i].Value = i*2; cout << "Element 的內容是:\n"; ShowElement(pE); delete [] pE; return 0; }

52 執行結果 請輸入Element的數目: 5 Element的內容是:

53 雙向鏈結串列 (doubly- linked list)
能夠自由在串列中往返尋找。 每個節點內都含有兩個指標,分別指向下一個節點及上一個節點。

54 「雙向鏈結串列」的宣告 struct Node { int Value; Node* Previous; Node* Next; };

55 樹狀結構 (tree) 二維結構。 開頭的節點稱為「根」(root)。 「層」(layer): 與根的距離相同的所有節點。 「葉」(leaves): 不再指向下一層節點的所有節點。

56 二元樹 (binary tree) 由於每個節點都只有兩個指標向下一層的兩個節點,形成一個「品」字型的局部結構,因此稱為「二元樹」。

57 union資料型態 可以在其內擁有多種資料型態,但一次只能有一種資料型態。例如: union Data {
float FloatValue; double DoubleValue; char CharValue; int IntValue; }; Data D1; 每個Data變數擁有的記憶空間由佔有最大空間的成員 (此例為D1.DoubleValue) 所決定。使用union的目的在於節省記憶體。

58 enum資料型態 enum是enumerate (列舉) 的簡寫。
由enum所定義的任何實例 (instance) 只能擁有當初enum宣告時所列舉的值之一。例如: enum Direction {Up, Down, Left, Right}; Direction x; 我們只能把這四種可能的值指定給x。例如: x = Right; 在電腦內部 {Up, Down, Left, Right} 分別和 {0, 1, 2, 3} 比對。

59 可以在宣告enum 時給予各成員確定的數值
例如: enum Check {Error = -1, Suspicious, Acceptable = 5, OK = 10}; 沒有特別指定的的值為其前一個成員 (亦即Error) 的值加1, 因此suspicious的內值為 –1 + 1 = 0。

60 enum資料型態 使用enum型態的目的是為了增進程式的可讀性,常與switch和if等判斷式結合使用。例如: int N;
cin >> N; switch (N) { case Up: cout << “Moving Up!\n”; break; case Down: cout << “Moving Down!\n”; case Left: cout << “Moving Left!\n”;

61 case Right: cout << “Moving Right!\n”; break; default: cout << “Static\n”; }

62 範例程式 檔案 TestEnum.cpp case Up: cout << "Moving Up!\n"; break;
#include <iostream> using namespace std; enum Direction {Up, Down, Left, Right}; // 主程式 int main() { int N; cout << "請輸入期望的運動方向\n"; cout << "(0=Up, 1=Down, 2=Left, 3=Right):\n"; cin >> N; switch (N) case Up: cout << "Moving Up!\n"; break;

63 case Down: cout << "Moving Down!\n"; break; case Left: cout << "Moving Left!\n"; case Right: cout << "Moving Right!\n"; default: cout << "Static\n"; } return 0;

64 執行結果 請輸入期望的運動方向 (0=Up, 1=Down, 2=Left, 3=Right): 2 Moving Left!


Download ppt "struct 可以在同一個名稱下擁有多種資料型態。使用struct能讓資料的存取和處理更為靈活。"

Similar presentations


Ads by Google