780 likes | 978 Views
第 十一 章. 結構 (Structure). 大綱. 11-1 何謂結構 11-2 結構之定義 11-3 結構欄位之存取與結構之設定 11-4 結構在程式內之位置 11-5 欄位初值及陣列欄位 11-6 結構佔用記憶體之大小 11-7 巢狀結構 11-8 結構與指標. 11-9 結構與陣列 11-10 結構陣列與指標 11-11 結構與函數 11-12 結構與排序 11-13 union 結構 11-14 位元結構 11-15 enum 列舉資料型態 11-16 typedef 之使用 11-17 內建結構應用範例.
E N D
第十一章 結構(Structure)
大綱 • 11-1 何謂結構 • 11-2 結構之定義 • 11-3 結構欄位之存取與結構之設定 • 11-4 結構在程式內之位置 • 11-5 欄位初值及陣列欄位 • 11-6 結構佔用記憶體之大小 • 11-7 巢狀結構 • 11-8 結構與指標 • 11-9 結構與陣列 • 11-10 結構陣列與指標 • 11-11 結構與函數 • 11-12 結構與排序 • 11-13 union結構 • 11-14 位元結構 • 11-15 enum列舉資料型態 • 11-16 typedef之使用 • 11-17內建結構應用範例
11-1 何謂結構(Structure) • 將不同資料型態結合在一起的資料紀錄稱為結構(Structure)。 • 組成結構之變數稱為成員(Member) ,元素(Element)或欄位(Field)。 • 資訊的組成:最基礎單位為字元,由許多不同的字元組成欄位,多個欄位組成結構,許多相同之結構資料集合成檔案,不同的檔案再集合為一資料庫。 資 料 庫 檔 案 結 構 欄 位 字 元
11-2 結構之定義 • 基本格式(直接宣告結構變數) structstruct-type-name { type member-name 1; type member-name 2; type member-name 3; } structure-variable_1={成員初始值}, structure-variable_2; • 結構名稱(struct-type-name)或結構變數(structure-variable)可省略,但不可兩者皆省略。
基本格式 structstruct-type-name { type member-name 1; type member-name 2; type member-name 3; }; struct-type-name structure-variable_1, structure-variable_2; structure-variable_1={成員初始值};
結構宣告範例: struct rec{ int chi, eng, math, total; float ave; } Bill={60, 50, 80, 0, 0}, Mary; rec Monica={90, 50, 70, 0, 0}; Mary={80, 60, 70, 0, 0};
11.3 結構欄位之存取與結構之設定 • 設有下面之結構: struct rec{ int chi, eng, math, total; float ave; }; • 結構成員的存取方式: • 一般變數之存取 • 指標變數之存取
一般變數結構之存取 • 存取結構內之欄位或成員之方式只要在結構變數與欄位間加一小數點”‧”即可,稱點運算子(dot operator) 。 • rec Bill; //一般變數結構宣告 • Bill.chi = 90; //設定Bill結構成員chi的值 • cin >> Bill.eng; //輸入Bill結構成員eng的值 • Bill.math = random(100); //亂數設定Bill結構成員math的值 • Bill.total = Bill.chi + Bill.eng + Bill.math; //運算式設定Bill結構成員total的值 • Bill.ave = Bill.total / 3.0; //運算式設定Bill結構成員ave的值 • cout << “chi=” << Bill.chi; //輸出Bill結構成員chi的值
指標變數結構之存取 • 當結構設定為指標變數時須以new要求分配記憶體,而要存取各欄位時則須在指標結構變數與欄位間加上動態指示符號(或箭頭運算子)”->”即可,一個減號”-“加上一個大於”>”符號。 • rec *Bill; //指標結構宣告 • Bill = new rec; //分配記憶體 5×4 bytes • Bill->chi = 90; //設定指標結構Bill成員chi的值 • cin >> Bill->eng; //輸入設定指標結構Bill成員eng的值 • Bill->math = random(100);//亂數設定指標結構Bill成員math的值 • Bill->total = Bill->chi + Bill->eng + Bill->math; • //運算設定指標結構Bill成員total的值 • Bill->ave = Bill->total / 3.0; • cout << “chi=” << Bill->chi;//輸出指標結構Bill成員chi的值
結構之設定 • 指標變數結構的設定 rec Bill, *Lisa, *Jone; Bill.chi = 98; Lisa = &Bill; Jone = Lisa; • 將一般變數結構Bill之資料設定給指標變數結構Lisa • 將指標變數結構Lisa之資料設定給指標變數結構Jone • 設有下面之結構: struct rec { int chi, eng, math, total; float ave; }; • 一般變數結構的設定 rec Bill, Jone; Bill.chi = 98; Jone = Bill; • 將Bill之資料設定給Jone
11-4 結構在程式內之位置 • 結構之定義可為:公用結構 與私用結構 • 公用結構 struct 結構名稱{ 結構成員; }; void main(){ 結構宣告; } • 私用結構 void main(){ struct 結構名稱 { 結構成員; }; 結構宣告; }
例題:一般結構的定義(公用結構)。 Bill.total = Bill.chi + Bill.eng + Bill.math; Bill.ave = Bill.total / 3.0; //求平均 cout << "Bill的成績:"; cout <<"\n國文="<<Bill.chi; cout <<"\n英文="<<Bill.eng; cout <<"\n數學="<<Bill.math; cout <<"\n總分="<<Bill.total; cout <<"\n平均="<< Bill.ave<<"\n"; return 0; } #include <iostream> //cout using namespace std; struct rec{ int chi, eng, math, total; float ave; }; //公用結構 int main( ) { rec Bill; Bill.chi=90; Bill.eng=80; Bill.math=87; //設定分數;
例題:一般結構的定義(私用結構)。 Bill.total = Bill.chi + Bill.eng + Bill.math; Bill.ave = Bill.total / 3.0; //求平均 cout << "Bill的成績:"; cout <<"\n國文="<<Bill.chi; cout <<"\n英文="<<Bill.eng; cout <<"\n數學="<<Bill.math; cout <<"\n總分="<<Bill.total; cout <<“\n平均="<< Bill.ave<<"\n"; return 0; } #include <iostream> //cout using namespace std; int main( ) { struct rec{ int chi, eng, math, total; float ave; }Bill={90,80,87,0,0}; //私用結構 //直接設定分數;
11-5 欄位初值及陣列欄位 • 結構之各欄位在定義時可事先設定初值 • struct rec • { int chi,eng, math, total; • float ave; • }Bill = {80, 90, 70, 0, 0}; 或Bill = {80, 90, 70}; • 則各常數會依序放入各欄位,即Bill.chi為80,Bill.eng為90,Bill.math為70,Bill.math為0,Bill.ave為0;所設定之常數個數不得多於結構欄位之個數,但可少於結構欄位之個數。 • 也可在宣告時再給值,如: • rec Bill = { 80, 90, 70, 0, 0};
陣列欄位 • 一般變數可宣告陣列,在結構內欄位也可以宣告為陣列,因陣列為同樣資料型態,故可把相同型態之欄位整合為陣列來存取,如將前述結構定義內欄位chi, eng, math以陣列來表示,設為score[3], 在使用上需自行注意陣列各元素代表那一欄位,如令score[0]代表chi, score[1]代表eng, score[2]代表math等。 • struct rec • { int score[3], total; • float ave; • } Evert; • 欄位陣列元素之存取為: • Evert.score[0], Evert.score[1], Evert.score[2] • 或用迴圈 • for (n=0; n<3; n++) • cin>>Evert.score[n];
例題:陣列欄位之使用。 for (int n=0; n<3; n++) Bill.total += Bill.score[n]; //計算總分 Bill.ave = Bill.total / 3.0; //求平均 cout << "Bill的成績:"; cout <<"\n國文="<< Bill.score[0]; cout <<"\n英文="<< Bill.score[1]; cout <<"\n數學="<< Bill.score[2]; cout <<"\n總分="<<Bill.total; cout <<“\n平均="<< Bill.ave<<"\n"; return 0; } #include <iostream> //cout using namespace std; struct rec{ int score[3],total; float ave; }; int main( ) { rec Bill; Bill.score[0] =90; Bill.score[1] =80; Bill.score[2] =87; Bill.total =0; //設定分數;
例題:陣列欄位及字串欄位之使用。 Bill.total =0; for (int n=0; n<3; n++) Bill.total += Bill.score[n]; Bill.ave = Bill.total / 3.0; cout << Bill.name <<"的成績:"; cout <<"\n國文(C)="<< Bill.score[0]; cout <<"\n英文(E)="<< Bill.score[1]; cout <<"\n數學(M)="<< Bill.score[2]; cout <<"\n總分="<<Bill.total; cout <<“\n平均="<< Bill.ave<<"\n"; return 0; } #include <iostream> //cout using namespace std; struct rec{ char name[20]; int score[3],total; float ave;}; int main( ) { rec Bill; cout <<“Input name:”; cin >> Bill.name; cout <<“Input C,E,M scores:”; cin >> Bill.score[0]; cin >> Bill.score[1]; cin >> Bill.score[2];
11-6 結構佔用記憶體之大小 • 不論結構定義為公用或私用,在電腦內所佔用之記憶體是一樣的。 • struct rec{ int chi, eng, math, total; float ave; }; • 每個int 型態佔用4 bytes共4×4=16 bytes • 每個float 型態佔用4 bytes共1×4=4 bytes • 結構共佔16+4=20 bytes
11-7 巢狀結構 • 迴圈有巢狀迴圈,條件或函數也各可包含條件或函數以形成巢狀敘述, 結構亦然,被包含者需事先定義在包含者之前,而包含者內需有一欄位宣告為屬於該結構。 • 欲將座號(seat)、姓名(name)定義為一結構,並使其成為國文、英文、數學、總分與平均結構內之一欄位,其定義宣告與存取方式如下所示: struct base { char seat[3]; //座號 char name[10]; //姓名 }; struct rec { base basic; //宣告basic為base結構 int chi, eng, math, total; float ave; }; • 若宣告 rec Bill;則結構變數Bill有一欄位basic屬於base之結構。欲存取base結構之欄位為: • Bill.basic.seat • Bill.basic.name
例題:將座號與姓名欄位當另一結構之欄位 。 Bill.chi=90; Bill.eng=80; Bill.math=87; Bill.total=Bill.chi+Bill.eng+Bill.math; Bill.ave = Bill.total / 3.0; cout << “座號:” << Bill.basic.seat; cout << “姓名:“<< Bill.basic.name; cout <<"\n國文="<< Bill.chi; cout <<"\n英文="<< Bill.eng; cout <<"\n數學="<< Bill.math; cout <<"\n總分="<<Bill.total; cout <<“\n平均="<< Bill.ave<<"\n"; return 0; } #include <iostream> //cout using namespace std; struct base{ char seat[3]; char name[10]; }; struct rec{ base basic; int chi, eng, math, total; float ave; }; int main( ) { rec Bill; cout <<“Seat and Input name”; cin >> Bill.basic.seat >> Bill.basic.name;
11-8 結構與指標 • 指標在處理資料時有兩種方式,即指向已存在之位址及配置記憶體,指標用在結構上亦然。 • 以宣告rec *Bill, a;為例: • *Bill為結構指標變數, a為一般結構變數, 則可將一般結構變數a之位址設定給指標變數Bill, 敘述為: Bill = &a;
例題:以指標宣告為結構,並指向一已存在之結構變數 。 Bill -> total = Bill -> chi + Bill -> eng + Bill -> math; Bill -> ave = Bill -> total / 3.0; cout << "Bill的成績:"; cout <<"\n國文="<<a.chi; cout <<"\n英文="<< Bill-> eng; cout <<"\n數學="<<Bill -> math; cout <<"\n總分="<<a.total; cout <<“\n平均="<< a.ave<<"\n"; return 0; } #include <iostream> //cout using namespace std; struct rec{ int chi, eng, math, total; float ave; }; int main( ) { rec *Bill, a; Bill = &a; Bill -> chi =90; (*Bill).eng =80; Bill -> math =87;
配置記憶體 • 配置記憶體之敘述為new, 處理完後釋放記憶體之敘述為delete, 兩者須成對出現。 • 指標結構變數 = new結構型態 [大小]; • delete [ ] 指標結構變數; • 如配置一塊記憶體: • Bill = new rec; //要求一塊rec大小之記憶體 • delete Bill; //釋放記憶體 • 如配置n塊記憶體: • Bill = new rec[n]; //要求n塊rec大小之記憶體 • delete [ ] Bill; //釋放記憶體
存取結構之欄位 • 不論指標以何種方式處理,欲存取結構之欄位有下面兩種方法: • 指標變數->結構欄位;即Bill->chi=90; • (*指標變數).結構欄位; 即(*Bill).chi=90; • 若為指標結構陣列,要設定第二元素,即: • 指標變數[1] ->結構欄位;即Bill[1] -> chi=90; • (*指標變數[1]).結構欄位;即(*Bill[1]).chi=90;
例題:以指標宣告為結構,要求配置記憶體 。 Bill -> total = Bill -> chi + Bill -> eng + Bill -> math; Bill -> ave = Bill -> total / 3.0; //平均 cout << "Bill 的成績:"; cout <<"\n國文="<<Bill -> chi; cout <<"\n英文="<<Bill -> eng; cout <<"\n數學="<<Bill -> math; cout <<"\n總分="<<Bill -> total; cout <<"\n平均="<<Bill -> ave << "\n"; delete Bill; //釋放記憶體 return 0; } #include <iostream> //cout using namespace std; struct rec{ int chi, eng, math, total; float ave;}; int main( ) {rec *Bill; //指標結構 Bill = new rec; //配置記憶體 Bill-> chi = 90; (*Bill).eng = 80; Bill->math = 87;
11-9 結構與陣列 • 一個結構只能處理一筆資料,若將結構以陣列方式來宣告,將可同時完成許多筆的結構資料,如結構資料的排序等。 • 宣告方式 • 結構名稱 結構變數[大小]; • 宣告 rec a[5]; • 表a陣列有五個結構,依序為a[0],a[1],a[2],a[3],a[4]等,各陣列元素皆為結構。
chi eng math total ave a[0] a[1] a[2] a[3] a[4] • 宣告 rec a[5];
結構陣列存取 • 若欲存取a[0]元素各欄位,其方式為: • a[0].chi, a[0].eng, a[0].math, a[0].total, a[0].ave; 其它各元素a[1], a[2], a[3], a[4]均同。 • 而非a.chi[0]、a.eng[0] • 例: • a[0].chi=90; • a[2].math=80;
例題:以結構陣列處理多筆資料,標準陣列方式存取 。 for (i=0; i<N; i++){ student[i].total = student[i].chi + student[i].eng + student[i].math; student[i].ave = student[i].total / 3.0; } cout << "學生的成績:\n"; cout <<"國文 英文 數學 總分 平均\n"; for (i=0; i<N; i++){ cout << setw(4) << student[i].chi; cout << setw(5) << student[i].eng; cout << setw(5) << student[i].math; cout << setw(5) << student[i].total; cout << setw(8) << student[i].ave << "\n"; } return 0;} #include <iostream> //cout #include <iomanip> using namespace std; struct rec{ int chi, eng, math, total; float ave;}; int main( ) {rec student[5]; //結構陣列 int i, N=5; for (i=0; i<N; i++){ cout <<"第"<< i <<"位chi="; cin >> student[i].chi; cout <<"第"<< i <<"位eng="; cin >> student[i].eng; cout <<"第"<< i <<"位math="; cin >> student[i].math; }
結構陣列名稱存取 • 另一種存取方式為把陣列名稱當指標看待,如: (a+0) -> chi; (a+0) -> eng; (a+0) -> math; (a+0) -> total; (a+0) -> ave; • 例: (a+i) -> chi=90; (a+i) -> math=80; • 把結構陣列名稱當指標看待要存取各欄位時需有指向運算子「->」,不可再加上指標運算子「*」在指標變數上。 • (a+i) -> chi內之「->」已代表指標,若再加上 *(a+i) -> chi 或 *(a+i).chi 就會有型態不對出現。
例題:結構陣列之處理以陣列變數名稱配合指向運算子存取各欄位 。 • cout <<"第"<<i+1<<"位 chi="; • cin >> (student+i)->chi; • cout <<"第"<<i+1<<"位 eng="; • cin >> (student+i)->eng; • cout <<"第"<<i+1<<"位math="; • cin >> (student+i)->math; • } • for(i=0;i<N;i++){ • (student+i)->total = (student+i)-> chi+(student+i) -> eng+(student+i) -> math; • (student+i)->ave=(student+i)->total/3.0; • } • #include <iostream> //cout • #include <iomanip> • using namespace std; • struct rec{ • char seat[3]; • int chi, eng, math, total; • float ave; • }; • int main(){ • int i, N=3; • rec student[5]; //結構陣列 • for(i=0;i<N;i++){ • cout <<"第"<<i+1<<"位座號="; • cin >> (student+i)->seat;
例題:結構陣列之處理以陣列變數名稱配合指向運算子存取各欄位 。 • cout << "學生的成績:\n"; • cout <<"座號 國文 英文 數學 總分 平均\n"; • for(i=0;i<N;i++){ • cout<<setw(3)<<(student+i)->seat; • cout<<setw(5)<<(student+i)->chi; • cout<<setw(5)<<(student+i)->eng; • cout<<setw(5)<<(student+i)->math; • cout<<setw(5)<<(student+i)->total; • cout<<setw(6)<<(student+i)->ave<<"\n"; • } • return 0; • }
11-10 結構陣列與指標 • 結構陣列若能以指標方式來存取,不但可減少佔用記憶體,使用完後又可將記憶體釋回。陣列指標之大小則不必受到固定之限制,可因需要由使用者自行決定,充分達到彈性之目的。 • 固定大小之配置 • 指向已存在之固定靜態結構陣列 • 彈性大小配置
固定大小之配置 • 指標結構陣列之大小在程式內已事先設定 • const int N=5; • rec *Bill; //宣告指標結構 • Bill = new rec[N]; //要求N塊結構之記憶體 • delete [ ] Bill;
例題:以結構指標變數要求配置固定記憶體。 cout <<"第"<< i <<"位 chi="; cin >> student[i].chi; cout <<"第"<< i <<"位 eng="; cin >> (student+i) -> eng; cout <<"第"<< i <<"位math="; cin >> (student+i) -> math;} for (i=0; i<N; i++){ (student+i) -> total = (student+i) -> chi + (student+i) -> eng + (student+i) -> math; (student+i) -> ave = (student+i) -> total / 3.0; } #include <iostream> #include <iomanip> //setw using namespace std; struct rec{ char seat[3]; int chi, eng, math, total; float ave;}; int main( ) {rec *student; student = new rec[N]; //配置N個結構陣列 int i; for (i=0; i<N; i++){ cout <<"第"<< i <<"位座號="; cin >> (student+i) -> seat;
例題:以結構指標變數要求配置固定記憶體。(續)例題:以結構指標變數要求配置固定記憶體。(續) cout << "學生的成績:\n"; cout <<"座號 國文 英文 數學 總分 平均\n"; //輸出各欄位分數 for (i=0; i<N; i++){ cout<< setw(3) << student[i].seat; cout<< setw(5) << (student+i) -> chi; cout<< setw(5) << (student+i) -> eng; cout<< setw(5) << (student+i) -> math; cout<< setw(5) << (student+i) -> total; cout<< setw(6) << (student+i) -> ave << "\n"; } delete [ ] student; return 0; } 學生的成績: 座號 國文 英文 數學 總分 平均 00 80 70 80 230 76.6667 01 50 60 70 180 60 02 90 70 60 220 73.3333 03 65 75 80 220 73.3333 04 80 76 86 242 80.6667
指向已存在之固定靜態結構陣列 • 當陣列已知,只是想將該陣列設定給指標之方式,如: • rec *Bill, a[5]; • Bill = a; • 下面是錯的,因為陣列名稱是位址。 • Bill = &a;
彈性大小配置 • 程式執行時由使用者決定所需或程式經計算來配置記憶體,是一種彈性的方式。 • int size; • rec *Bill; //宣告指標結構 • cin >> size; //輸入陣列大小 • Bill = new rec[size]; //要求size塊結構之記憶體 • delete [ ] Bill;
11-11 結構與函數 • 結構在與函數溝通傳遞資料時,除可用欄位當一般變數處理外,亦可將整個結構做為傳送之單位。 • 以欄位當參數傳遞 • 以結構當參數傳遞 • 傳址之一般結構變數 • 指標變數之互傳 • 函數之參數為以位址接收 • 傳值之一般結構變數 • 以函數名稱傳回結構 • 函數名稱以一般結構變數傳回 • 函數名稱以指標結構變數傳回 • 傳回指標結構陣列
例題:以欄位方式傳遞資料。 float get_ave(int score){ return score / 3.0; } int get_data(int *a, int *b, int *c){ *a = 90; *b = 80; *c = 70; return (*a+*b+*c); } void output_data(int a, int b, int c, int d, float ave){ cout << "國文=" << a; cout << "\n英文=“ << b; cout << "\n數學=“ << c; cout << "\n總分=“ << d; cout << "\n平均=“ << ave << "\n"; } #include <iostream> using namespace std; int get_data(int*, int*, int*); float get_ave(int); void output_data(int, int, int, int, float); struct rec{ int chi, eng, math, total; float ave;}; int main( ){ rec Bill; Bill.total = get_data(&Bill.chi, &Bill.eng, &Bill.math); Bill.ave = get_ave(Bill.total); output_data(Bill.chi, Bill.eng, Bill.math, Bill.total, Bill.ave); return 0; }
呼叫之參數 函數之參數 1 &一般結構變數 *指標變數 2 指標變數 *指標變數 3 一般結構變數 &一般結構變數 4 一般結構變數 一般結構變數 以結構當參數傳遞 有下面幾種方式:
以函數名稱傳回結構 • 結構對C++而言是一種型態,與對待一般資料型態一樣,因此可以以一般變數傳回或以指標傳回。 • 一般變數傳回 結構變數 = 函數名稱(參數); //呼叫 結構名稱 函數名稱(參數); //函數定義 { return 結構變數; }
例題:以函數名稱傳回結構,在函數設定結構資料後傳回。例題:以函數名稱傳回結構,在函數設定結構資料後傳回。 #include <iostream> using namespace std; struct rec{ int x, y; }; rec get_rec( ); //原型宣告 void main( ) { rec a; a = get_rec( ); //取得一般結構 cout << "a.x="<< a.x << endl << "a.y="<< a.y << endl; return 0; } rec get_rec( ) { rec t; t.x = 80; t.y = 77; return t; } a.x=80 a.y=77
以函數名稱傳回結構 • 指標傳回 指標結構變數 = 函數名稱(參數); //呼叫 結構名稱 *函數名稱(參數); //函數定義 { return 指標結構變數; }
例題:以函數名稱傳回結構,在函數設定結構資料後傳回。例題:以函數名稱傳回結構,在函數設定結構資料後傳回。 #include <iostream> using namespace std; struct rec { int x,y; }; rec *get_rec(); //原型宣告 void main(){ rec *a; a = get_rec(); cout << "a->x=" << a->x << endl << "a->y=" << a->y <<endl; delete a; } rec *get_rec(){ rec *t = new rec; //配置記憶體 t->x = 80; t->y = 77; return t; }
11-12 結構與排序 • 由於結構之排序需以某一欄位之值來比較大小,無論由小到大或由大到小之排列,皆牽涉到整個結構內各欄位之互換動作,若結構欄位太多要一個一個欄位來互換會顯得沒有效率,而且容易寫錯,所以結構之互換是以整個進行。 • 結構之互換 • 結構之排序
結構之互換 • rec a, b, t; //三個同樣型態之結構 • 欄位間的互換 t.chi = a.chi; a.chi = b.chi; b.chi = t.chi; • 整個結構之互換 t = a; a = b; b = t;
結構之排序(1) • 排序是以氣泡排序法(Bubble sorting)為主: • 設結構 struct rec{ short seat; //座號 int chi, eng, math, total; float ave; short rank; //名次 }; • 數入資料
結構之排序(2) • 依總分total排序由大到小 for (i=0; i< N-1; i++) for (j=0; j< N-i-1; j++) if (student[j].total < student[j+1].total) { rec t; t = student[j]; student[j] = student[j+1]; student[j+1] = t; }
結構之排序(3) • 寫名次1,2,3,...10 到rank欄位 for (i=0; i< N; i++) student[i].rank = i+1;