- Visual C++實用教程
- 鄭阿奇編著
- 4199字
- 2018-12-30 12:04:33
1.9 結構、共用和自定義
程序中所描述的數據往往來源于日常生活,比如一個學生有多門課程成績,此時用一維數組來組織數據則可滿足需要。若是多個學生有多門課程成績,則此時用二維數組來組織仍可滿足,但若還有每門課程的學分數據,則用三維數組就無法反映其對應關系了。事實上,可將數據這個概念擴展為信息,每條信息看做一條記錄。顯然,對記錄的描述就不能用簡單的一維或多維數組來組織,而應該使用從C語言繼承下來的結構體類型來構成。除結構體之外,C++還允許構造共用體等類型,它們從另一個方面來詮釋數據類型的構造方法。
1.9.1 結構體
結構體是從C語言繼承下來的一種構造數據類型,它是由多種類型的數據(變量)組成的整體。組成結構類型的各個分量稱為結構的數據成員(簡稱為成員,或稱為成員變量)。
1.結構類型聲明
在C++中,結構類型的聲明可按下列格式進行:
struct [結構類型名] { <成員定義1>; <成員定義2>; … <成員定義n>; };
結構類型聲明是以關鍵字struct開始的,結構類型名應是一個有效的合法的標識符,若該結構類型變量以后不再定義,結構類型名也可不指定。結構體中的每個成員都必須通過成員定義來確定其數據類型和成員名。要注意:
(1)成員的數據類型可以是基本數據類型,也可以是數組、結構等構造類型或其他已聲明的合法的數據類型。
(2)結構類型的聲明僅僅是一個數據類型的說明,編譯不會為其分配內存空間,只有當用結構類型定義結構類型的變量時,編譯才會為這種變量分配內存空間。
(3)結構類型聲明中最后的分號“;”不要漏掉。
例如,若聲明的學生成績結構類型為:
struct STUDENT { int no; // 學號 float score[3]; // 三門課程成績 float edit[3]; // 三門課程的學分 float total,ave; // 總成績和平均成績 float alledit; // 總學分 }; // 分號不能漏掉
則結構體中的成員變量有no(學號)、score[3](三門課程成績)、edit[3](三門課程的學分)、total (總成績)、ave(平均成績)和alledit(總學分)。需要說明的是:
(1)在結構體中,成員變量定義與一般變量定義規則相同。例如,若成員變量的數據類型相同,可寫在一行定義語句中,如total和ave成員變量的定義。
(2)結構體中的成員變量的定義次序只會影響在內存空間中的分配順序(當定義該結構類型變量時),而對所聲明的結構類型沒有影響。
(3)結構類型名是區分不同類型的標志。例如,若再聲明一個結構類型PERSON,其成員變量都與STUDENT相同,但卻是兩個不同的結構類型。結構類型名通常用大寫來表示,以便與其他類型名相區別。
2.結構類型變量的定義
一旦在程序中聲明了一個結構類型,就為程序增添了一種新的數據類型,也就可以用這種數據類型定義該結構類型的變量。雖然結構類型變量的定義與基本數據類型定義基本相同,但也有一些區別。在C++中,定義一個結構類型變量可有3種方式。
(1)先聲明結構類型,再定義結構類型變量,稱為聲明之后定義方式(推薦方式)。
這種方式與基本數據類型定義格式相同,即:
[struct]<結構類型名> <變量名1>[,<變量名2>, … <變量名n>];
例如:
struct STUDENT stu1,stu2;
其中,結構類型名STUDENT前面的關鍵字struct可以省略。一旦定義了結構類型變量,編譯就會為其分配相應的內存空間,其內存空間的大小就是聲明時指定的各個成員所占的內存空間的大小之和。
(2)在結構類型聲明的同時定義結構類型變量,稱為聲明之時定義方式。
這種方式是將結構類型的聲明和變量的定義同時進行。在格式上,被定義的結構類型變量名應寫在最后的花括號和分號之間,多個變量名之間要用逗號隔開。例如:
struct STUDENT { //… }stu1,stu2; // 定義結構類型變量
(3)在聲明結構類型時,省略結構類型名,直接定義結構類型變量。
由于這種方式一般只用于程序不再二次使用該結構類型的場合,因此稱這方式為一次性定義方式。例如:
struct { //… }stu1,stu2; // 定義結構類型變量
此時應將左花括號“{”和關鍵字struct寫在一行上,以便與其他方式相區別,也增強了程序的可讀性。
3.結構類型變量的初始化
與一般變量和數組一樣,結構類型變量也允許在定義的同時賦初值,即結構類型變量的初始化,其一般形式是在定義的結構類型變量后面加上“= {<初值列表>};”。例如:
STUDENT stu1={1001,90,95,75,3,2,2};
它是將花括號中的初值按其成員變量定義的順序依次給成員變量賦初值,也就是說,此時stu1中的no = 1001,score[0] = 90,score[1] = 95,score[2] = 75,edit[0] = 3,edit[1] = 2,edit[2] =2。由于其他成員變量的初值未被指定,因此它們的值是默認值或不確定。
需要說明的是,可以在上述stu1的初值列表中,適當地增加一些花括號,以增加可讀性,如stu1的成員score和edit都是一維數組,因此可以這樣初始化:
STUDENT stu1={1001,{90,95,75},{3,2,2}};
此時初值中花括號僅起分隔作用。但若是對結構類型數組進行初始化,則不能這么做。
前面已提及,結構類型中成員的數據類型還可以是另一個已定義的結構類型,例如:
struct POINT { int x, y; }; struct RECT { POINT ptLeftTop; // 使用已定義過的結構類型POINT int nWidth; int nHeight; };
此時對RECT變量初始化時,可使用花括號來增強其可讀性。例如:
RECT rc1={{20,30},100,80}; // 里面的花括號是為了增強可讀性
4.結構類型變量的引用
當一個結構類型變量定義后,就可引用這個變量。使用時,應遵循下列規則:
(1)只能引用結構類型變量中的成員變量,并使用下列格式:
<結構體變量名>.<成員變量名>
例如:
struct POINT { int x, y; } spot = {20, 30}; cout<<spot.x<<spot.y;
其中,“.”是成員運算符,它的優先級很高,僅次于域運算符“::”,因而可以把spot.x和spot.y作為一個整體來看待,它可以像普通變量那樣進行賦值或進行其他各種運算。
(2)若成員本身又是一個結構類型變量,則引用時需要用多個成員運算符一級一級地找到最低一級的成員。例如:
struct RECT { POINT ptLeftTop; POINT ptRightDown; } rc = {{10,20},{40,50}};
則有:
cout<<rc.ptLeftTop.x<< rc.ptLeftTop.y;
(3)多數情況下,結構類型相同的變量之間可以直接賦值,這種賦值等效于各個成員的依次賦值。如:
struct POINT { int x, y; }; POINT pt1={10,20}; POINT pt2=pt1; // 將pt1直接賦給pt2 cout<<pt2.x<<"\t"<<pt2.y<<endl; // 輸出 10 20
其中,pt2 = pt1等效于pt2.x = pt1.x; pt2.y = pt1.y;
數組是相同數據類型的元素的集合,當然元素的數據類型也可以是結構類型。由結構類型的元素組成的數組稱為結構數組。
1.結構數組的初始化
在定義結構數組的同時,也可對其進行初始化,其方法與數組相同。但要注意,由于結構類型聲明的是一條記錄信息,而一條記錄在二維線性表中就表示一個行,因此一維結構數組的初始化形式應與二維普通數組相同。例如:
struct STUDENT { int no; // 學號 float score[3]; // 三門課程成績 float edit[3]; // 三門課程的學分 float total,ave; // 總成績和平均成績 float alledit; // 總學分 }; STUDENT stu[3]={{1001,90,95,75,3,2,2}, {1002, 80, 90, 78, 3, 2, 2}, {1003, 75, 80, 72, 3, 2, 2}};
1.9.2 結構數組
此時初值中的花括號起到類似二維數組中的行的作用,并與二維數組初始化中的花括號的使用規則相同。這里依次將初值中的第1對花括號里的數值賦給元素stu[0]中的成員,將初值中的第2對花括號里的數值賦給元素stu[1]中的成員,將初值中的第3對花括號里的數值賦給元素stu[2]中的成員。需要說明的是,與普通數組初始化相同,在結構數組初始化中,凡成員未被指定初值時,這些成員的初值均為0。
2.結構數組元素的引用
一旦定義了結構數組,就可以在程序中引用結構數組元素。由于結構數組元素等同于一個同類型的結構變量,因此它的引用與結構變量相類似,格式如下:
<結構數組名>[<下標表達式>].<成員>
例如:
for (int i=0; i< sizeof(stu)/sizeof(STUDENT); i++) { stu[i].total =stu[i].score[0]+stu[i].score[1]+stu[i].score[2]; stu[i].ave =stu[i].total/3.0; stu[i].alledit =stu[i].edit[0]+stu[i].edit[1]+stu[i].edit[2]; if (stu[i].ave > stu[nMax].ave) nMax = i; }
1.9.3 結構與函數
當結構類型變量作為函數的參數時,它與普通變量一樣,由于結構類型變量不是地址,因此這種傳遞是值傳遞方式,整個結構都將被復制到形參中去。
【例Ex_StructValue】 將結構體的值作為參數傳給函數
#include <iostream.h> struct PERSON { int age; // 年齡 float weight; // 體重 char name[25]; // 姓名 }; void print(PERSON one) { cout <<one.name<<"\t" <<one.age<<"\t" <<one.weight<<"\n"; } PERSON all[4]={ {20,60,"Zhang"}, {28,50,"Fang"}, {33,78,"Ding"}, {19,65,"Chen"}}; int main() { for(int i=0;i<4;i++) print(all[i]); return 0; }
程序運行結果如下:
Zhang 20 60
Fang 28 50
Ding 33 78
Chen 19 65
print函數的參數是PERSON結構變量,main函數調用了4次print函數,實參為PERSON結構數組的元素。
事實上,結構體還可以作為一個函數的返回值。
【例Ex_StructReturn】 將結構體的值作為參數傳給函數
#include <iostream.h> struct PERSON { int age; // 年齡 float weight; // 體重 char name[25]; // 姓名 }; void print(PERSON one) { cout <<one.name<<"\t" <<one.age<<"\t" <<one.weight<<"\n"; } PERSON getperson() { PERSON temp; cout<<"請輸入姓名、年齡和體重:"; cin>>temp.name>>temp.age>>temp.weight; return temp; } int main() { PERSON one=getperson(); print(one); return 0; }
程序運行結果如下:
請輸入姓名、年齡和體重:ding 419
0?
ding 41 90
由于函數getperson返回的是一個結構類型的值,因此可以先在函數中定義一個局部作用域的臨時結構體變量temp,當用戶輸入的數據保存在temp后,通過return返回。
1.9.4 結構指針
當定義一個指針變量的數據類型是結構類型時,這樣的指針變量就稱為結構指針變量,它指向結構體類型變量。
【例Ex_StructPointer】 指針在結構體中的應用
#include <iostream.h> #include <string.h> struct PERSON { int age; // 年齡 char sex; // 性別 float weight; // 體重 char name[25]; // 姓名 }; int main() { struct PERSON one; struct PERSON *p; // 指向PERSON類型的指針變量 p=&one; p->age=32; p->sex=’M’; p->weight=(float)80.2; strcpy(p->name,"LiMing"); cout<<"姓名:"<<(*p).name<<endl; cout<<"性別:"<<(*p).sex<<endl; cout<<"年齡:"<<(*p).age<<endl; cout<<"體重(kg):"<<(*p).weight<<endl; return 0; }
程序運行結果如下:
姓名:LiMing
性別:M
年齡:32
體重(kg):80.2
程序中,“->”稱為指向運算符,它的左邊必須是一個指針變量,它等效于指針變量所指向的結構體類型變量,如p->name和(*p).name是等價的,都是引用結構PERSON類型變量one中的成員name,由于成員運算符“.”優先于“*”運算符,所以(*p).name中*p兩側的括號不能省略,否則*p.name與*(p.name)等價,但這里的*(p.name)是錯誤的。
若將結構體變量看成一個整體,那么指向結構體變量數組的指針操作和指向數組的指針操作是一樣的。例如若有:
PERSON many[10],*pp; pp=many; // 等價于pp=&many[0];
則pp+i與many+i是等價的,(pp+i)->name與many[i].name是等價的,等等。
事實上,結構指針變量也可作為函數的參數或返回值,由于其使用方法與一般指針變量相類似,因此這里不再贅述。
在C++中,共用體的功能和語句都和結構體相同,但它們最大的區別是:共用體在任一時刻只有一個成員處于活動狀態,且共用體變量所占的內存長度等于各個成員中最長成員的長度,而結構體變量所占的內存長度等于各個成員的長度之和。在共用體中,各個成員所占內存的字節數各不相同,但都是從同一地址開始的。
1.9.5 共用體
定義一個共用體可用下列格式:
union <共用體名> { <成員定義1>; <成員定義2>; … <成員定義n>; }[共用體變量名表]; // 注意最后的分號不要忘記
例如:
union NumericType { int iValue; // 整型變量,4字節長 long lValue; // 長整型變量,4字節長 float fValue; // 實型,8字節長 };
這時,系統為NumericType開辟了8字節的內存空間,因為成員fValue是實型,故它所占空間最大。需要說明的是,共用體除了關鍵字(union)與結構體不同外,其使用方法均與結構體相同。
【例Ex_Union】 共用體的使用
#include <iostream.h> #include <string.h> union PERSON { int age; // 年齡 float weight; // 體重 char name[25]; // 姓名 }; void print(PERSON one) { cout <<one.name<<"\t" <<one.age<<"\t" <<one.weight<<"\n"; } int main() { PERSON all={33}; // all.age=33 print(all); // 只有all.age有效 all.weight=80; print(all); // 只有all.weight有效 strcpy(all.name,"ding"); print(all); // 只有all.name有效 return 0; }
程序運行結果如下:
! 33 4.62428e-044 1117782016 80 ding 1735289188 1.12587e+024
1.9.6 使用typedef
在C++中可使用關鍵字typedef來為一個已定義的合法的類型名增加新名稱,從而使相同類型具有不同的類型名,這樣做的好處有兩個:一是可以按統一的命名規則定義一套類型名稱體系,從而可以提高程序的移植性;二是可以將一些難以理解的、冗長的數據類型名重新命名,使其變得容易理解和閱讀。例如,若為const char *類型名增加新的名稱CSTR,則在程序中不僅書寫方便,而且更具可讀性。這里就不同數據類型來說明typedef的使用方法。
1.為基本數據類型名添加新的類型名
當使用typedef為基本數據類型名增加新的名稱時,可使用下列格式:
typedef <基本數據類型名> <新的類型名>;
其功能是將新的類型名賦予基本數據類型的含義。其中,基本數據類型名可以是char、short、int、long、float、double等,也可以是帶有const、unsigned或其他修飾符的基本類型名。例如:
typedef int Int; typedef unsigned int UInt; typedef const int CInt;
注意:
書寫時typedef及類型名之間必須要有一個或多個空格,且一條typedef語句只能定義一個新的類型名。這樣,上述三條typedef語句就使得在原先基本數據類型名的基礎上增加了Int、UInt和CInt類型名。之后,就可直接使用這些新的類型名來定義變量了。例如:
UInt a,b; // 等效于unsigned int a,b; CInt c=8; // 等效于const int a=8;
再如,若有:
typedef short Int16; typedef int Int32;
則新的類型名Int16和Int32可分別反映16位和32位的整型。這在32位系統中,類型名和實際是吻合的。若在16位系統中,為了使Int16和Int32也具有上述含義,則可用typedef語句重新定義:
typedef int Int16; typedef long Int32;
這樣就保證了程序的可移植性。
2.為數組類型名增加新的類型名
當使用typedef為數組類型名增加新的名稱時,可使用下列格式:
typedef <數組類型名> <新的類型名>[<下標>];
其功能是將新的類型名作為一個數組類型名,下標用來指定數組的大小。例如:
typedef int Ints[10]; typedef float Floats[20];
則新的類型名Ints和Floats分別表示具有10個元素的整型數組類型和具有20個元素的單精度實型數組類型。這樣,若有:
Ints a; // 等效于int a[10]; Floats b; // 等效于float b[20];
3.為結構類型名增加新的類型名
當使用typedef為結構類型名增加新的類型名稱時,可使用下列格式:
typedef struct [結構類型名] { <成員定義>; … } <新的類型名> ;
這種格式是在結構類型聲明的同時進行的,其功能是將新的類型名作為此結構類型的一個新名稱。例如:
typedef struct student { … } STUDENT; STUDENT stu1; // 等效于struct student stu1;
4.為指針類型名增加新的類型名稱
由于指針類型不容易理解,因此typedef常用于指針類型名的重新命名。例如:
typedef int* PInt; typedef float* PFloat; typedef char* String; PInt a,b; // 等效于int*a,*b;
則PInt、PFloat和String分別被聲明成整型指針類型名、單精度實型指針類型名和字符指針類型名。由于字符指針類型常用來操作一個字符串,因此常將字符指針類型名聲明為String或STR。
可見,用typedef為一個已有的類型名聲明新的類型名稱的方法時可按下列步驟進行:
① 用已有的類型名寫出定義一個變量的格式,如int a;
② 在格式中將變量名換成要聲明的新的類型名稱,如int Int;
③ 在最前面添加上關鍵字typedef即可完成聲明,如typedef int Int;
④ 之后,就可使用新的類型名定義變量了。
需要說明的是,與struct、enum和union構造類型不同的是,typedef不能用于定義變量,也不會產生新的數據類型,它所聲明的僅僅是一個已有數據類型的別名。另外,typedef聲明的標識符也有作用域范圍,也遵循先聲明后使用的原則。
以上是C++語言最基礎的內容。下一章將討論C++所支持的面向對象的程序設計方法。
- C語言程序設計案例教程
- VMware View Security Essentials
- MongoDB for Java Developers
- Java從入門到精通(第5版)
- AIRAndroid應用開發實戰
- 算法精粹:經典計算機科學問題的Python實現
- Python漫游數學王國:高等數學、線性代數、數理統計及運籌學
- Serverless架構
- Oracle Exadata專家手冊
- 大學計算機基礎實驗指導
- 單片機C語言程序設計實訓100例
- Java程序員面試筆試寶典(第2版)
- Visual Basic程序設計習題與上機實踐
- Visual FoxPro 6.0程序設計
- IPython Interactive Computing and Visualization Cookbook