高級語言程序設(shè)計-課件-第7章-自定義數(shù)據(jù)類型_第1頁
高級語言程序設(shè)計-課件-第7章-自定義數(shù)據(jù)類型_第2頁
高級語言程序設(shè)計-課件-第7章-自定義數(shù)據(jù)類型_第3頁
高級語言程序設(shè)計-課件-第7章-自定義數(shù)據(jù)類型_第4頁
高級語言程序設(shè)計-課件-第7章-自定義數(shù)據(jù)類型_第5頁
已閱讀5頁,還剩58頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

第7章自定義數(shù)據(jù)類型

7.1求同存異:結(jié)構(gòu)體類型

7.2伙伴牽手:鏈表

7.3你中有我:共用體類型

7.4心中有數(shù):枚舉類型

7.5別名當?shù)溃簍ypedef類型

7.6本章小結(jié)7.1結(jié)構(gòu)體類型7.1.1結(jié)構(gòu)體類型的引入【案例7.1】如何使用程序?qū)崿F(xiàn)對歌曲排行榜的管理?首先需要使用一種數(shù)據(jù)類型表示上表的結(jié)構(gòu)。通過觀察表格,不難發(fā)現(xiàn)在表格“列”的方向上數(shù)據(jù)類型相同,回顧之前學過的數(shù)組概念及應(yīng)用時發(fā)現(xiàn),要想對現(xiàn)實世界中的一組或一系列相似的事物進行建模,數(shù)組是理想的工具。若采用數(shù)組結(jié)構(gòu),在實現(xiàn)時,可定義4個數(shù)組,分別存儲歌曲編號、歌曲名稱、歌手姓名和票數(shù),假設(shè)本期共有40首歌曲,初始化的代碼為:7.1結(jié)構(gòu)體類型intNo[40]={1001,1002,1003};charsong_name[40][32]={"MyOldClassmate","ThoseFlowers","Tomorrowwillbebetter"};charname[40][16]={"LaoLang","PuShu","LuoDayou"};/*歌手姓名*//*歌曲編號*//*歌曲名稱*/但存在的問題:(1)分配內(nèi)存不集中,尋址效率不高:每首歌曲本來是很統(tǒng)一的數(shù)據(jù)(表中的一行),使用數(shù)組后卻零散地分布在內(nèi)存中,查找效率不高。(2)對數(shù)組賦初值時,易發(fā)生錯位,而且一旦出錯,后面所有的數(shù)據(jù)也都會發(fā)生錯誤。(3)結(jié)構(gòu)非常零散,造成數(shù)據(jù)管理上的困難。希望的組織方式:從表格“行”(在數(shù)據(jù)庫中稱為“記錄”)的角度進行觀察,將每首歌曲的信息單獨集中在某一段內(nèi)存中進行存放。C語言提供了這樣一種數(shù)據(jù)類型,稱為結(jié)構(gòu)體,它和數(shù)組一樣,也屬于構(gòu)造數(shù)據(jù)類型。37.1結(jié)構(gòu)體類型7.1.2結(jié)構(gòu)體變量的定義、初始化和引用1.結(jié)構(gòu)體變量的定義struct結(jié)構(gòu)體類型名{數(shù)據(jù)類型數(shù)據(jù)類型…數(shù)據(jù)類型成員名1;成員名2;注意:在“聲明”過程中,并未定義任何結(jié)構(gòu)體變量,因而編譯器不為其分配內(nèi)存。成員名n;};其中,“struct”是聲明結(jié)構(gòu)體模板的關(guān)鍵字,其后是“結(jié)構(gòu)體類型名”,在“結(jié)構(gòu)體類型名”后的花括號中,聲明了結(jié)構(gòu)體類型的各成員項,每個成員由“數(shù)據(jù)類型”和“成員名”共同組成。“成員名”和程序中的其他變量名可以同名,互不干擾。最后的分號是結(jié)構(gòu)體聲明的結(jié)束標志,不可省略。structranking_list{intNo;/*歌曲編號*/charsong_name[32];charname[16];intnum;/*歌曲名稱*//*歌手姓名*//*票數(shù)*/4};7.1結(jié)構(gòu)體類型定義結(jié)構(gòu)體變量:(1)先聲明結(jié)構(gòu)體類型,再定義結(jié)構(gòu)體變量,其格式為:struct結(jié)構(gòu)體類型名結(jié)構(gòu)體變量名;例如:structranking_listrl;此時定義了結(jié)構(gòu)體變量rl,它具有歌曲編號No、歌曲名稱song_name、歌手姓名name和票數(shù)num這4個成員,且這些成員占據(jù)的是連續(xù)的內(nèi)存空間。(2)在定義類型的同時定義變量,如:struct{ranking_listintcharNosong_name;[32];charintnumname[;16];/*/*歌曲編號歌曲名稱*/*//*/*歌手姓名票數(shù)*/*/}rl;(3)直接定義結(jié)構(gòu)體變量(不指定結(jié)構(gòu)體類型名),如:struct{intcharNosong_name;[32];charintnumname[;16];/*/*歌曲編號歌曲名稱*/*//*/*歌手姓名票數(shù)*/*/}rl;該方法由于未給定結(jié)構(gòu)體類型名,是一個匿名結(jié)構(gòu)體,不能在程序的其5他地方定義結(jié)構(gòu)體變量,因此通用性不好,一般使用較少。7.1結(jié)構(gòu)體類型結(jié)構(gòu)體變量rl所占據(jù)的內(nèi)存空間可以利用表達式sizeof(rl)求出。【案例7.2】#include<stdio.h>structnum{計算結(jié)構(gòu)體變量所占據(jù)的內(nèi)存大小。chara;doubleb;intc;shortd;}S;intmain(){printf("結(jié)構(gòu)體變量S所占據(jù)的內(nèi)存大小為:%d\n",sizeof(S));return0;}結(jié)構(gòu)體變量占據(jù)的內(nèi)存大小是按照“字節(jié)對齊”的機制來分配的。字節(jié)對齊是指字節(jié)按照一定規(guī)則在空間上排列。該規(guī)則要同時滿足以下兩點:67.1結(jié)構(gòu)體類型(1)結(jié)構(gòu)體的每個成員變量相對于結(jié)構(gòu)體首地址的偏移量,是該成員變量的基本數(shù)據(jù)類型(不包括結(jié)構(gòu)體、數(shù)組等)大小的整數(shù)倍,如果不夠,編譯器會在成員之間加上填充字節(jié)。(2)結(jié)構(gòu)體的總大小為結(jié)構(gòu)體最寬基本類型成員大小的整數(shù)倍,如果不夠,編譯器會在最末一個成員之后加上填充字節(jié)。因此,剛才計算出結(jié)構(gòu)體變量S的內(nèi)存大小22并不符合這一點,本例成員變量中最寬基本類型的大小為8(sizeof(double)),所以成員d后面會被填充2字節(jié),使得最終結(jié)構(gòu)體變量S所占內(nèi)存大小為24字節(jié)。如果結(jié)構(gòu)體中有構(gòu)造類型變量,如結(jié)構(gòu)體中有int類型數(shù)組成員,則偏移量以數(shù)組中的元素類型為基準,即偏移量是4(sizeof(int))的倍數(shù)。77.1結(jié)構(gòu)體類型2.結(jié)構(gòu)體變量的初始化由于結(jié)構(gòu)體變量中存儲的是一組類型不同的數(shù)據(jù),因此,為結(jié)構(gòu)體變量初始化的過程其實就是為結(jié)構(gòu)體中各個成員初始化的過程。一般可以使用以下兩種方法。(1)在定義結(jié)構(gòu)體變量的同時直接進行初始化,例如:structranking_list{intNo;charsong_name[32];charname[16];intnum;/*歌曲編號*//*歌曲名稱*//*歌手姓名*//*票數(shù)*/}rl={1001,"MyOldClassmate","LaoLang",0};(2)聲明好結(jié)構(gòu)體類型模板后,對結(jié)構(gòu)體變量初始化,例如:structranking_list{intNo;charsong_name[32];charname[16];intnum;/*歌曲編號*//*歌曲名稱*//*歌手姓名*//*票數(shù)*/};structranking_listrl={1001,"MyOldClassmate","LaoLang",0};87.1結(jié)構(gòu)體類型如果繼續(xù)進行初始化,structranking_listrl2={1002,"ThoseFlowers","PuShu",0};那么,兩個獨立的結(jié)構(gòu)體變量rl和rl2都具有相同的structranking_list類型的結(jié)構(gòu),分別對應(yīng)于表中的兩行記錄信息,且它們之間可以相互賦值,例如:rl=rl2;/*注意:只有相同類型的結(jié)構(gòu)體變量間才可以相互賦值*/賦值過程是按照結(jié)構(gòu)體的成員順序?qū)ο鄳?yīng)成員逐一進行,賦值結(jié)束后,結(jié)構(gòu)體變量rl各成員和rl2各成員的值相同,都是{1002,"ThoseFlowers","PuShu",0}。3.結(jié)構(gòu)體變量的引用結(jié)構(gòu)體變量名.成員名;如:rl.num;其中,點號作為成員(分量)運算符,它在所有運算符中的級別最高,因此可將rl.num作為一個整體看待。一旦引用后,就可以將其作為同種類型的普通變量一樣使用,如可以進行賦值、自增自減、比較等,例如:rl.num++;97.1結(jié)構(gòu)體類型有了成員運算符后,在結(jié)構(gòu)體變量初始化時,就允許只對部分成員進行初始化,例如:structranking_listrl3={.No=1003};/*在成員名前加上成員運算符(點號)*/其中,花括號中的“.No”就是代表“rl3.No”,而其他未被初始化的成員中,若數(shù)值型成員被初始化為0,字符型成員被初始化為'\0',指針型成員被初始化為NULL。此外,對上述結(jié)構(gòu)體變量間的賦值語句“rl=rl2;”其效果等價于:rl.No=rl2.No;strcpy(rl.song_name,rl2.song_name);strcpy(,);rl.num=rl2.num;/*注意:應(yīng)使用字符串復制函數(shù)strcpy*/注意:無法通過結(jié)構(gòu)體變量名一次性地輸入或輸出結(jié)構(gòu)體變量中所有成員的值,只能通過引用對結(jié)構(gòu)體變量中的每個成員依次進行處理。例如:scanf("%d,%s,%s,%d\n",&rl);/*整體讀入結(jié)構(gòu)體變量rl各成員的值,不合法*/printf("%d,%s,%s,%d\n",rl);printf("%d,%s,%s,%d\n",rl.No,rl.song_name,,rl.num);/*輸出結(jié)構(gòu)體變量rl各成員的值,不合法*//*使用引用分別輸出結(jié)構(gòu)體變量rl各成員的值,合法*/scanf("%d",&rl.No);/*使用引用輸入結(jié)構(gòu)體變量rl中No成員的值,合法*/107.1結(jié)構(gòu)體類型4.嵌套的結(jié)構(gòu)體結(jié)構(gòu)如果在原先的“歌曲排行榜”表格中添加一列,用于記錄該首歌曲的創(chuàng)作日期,而且日期中又包含了年、月、日等詳細信息,就形成了嵌套結(jié)構(gòu)。這就要求在原有結(jié)構(gòu)體模板的基礎(chǔ)上做些修改,由于在定義結(jié)構(gòu)體成員時所用的數(shù)據(jù)類型也可以是結(jié)構(gòu)體類型,這樣就形成了結(jié)構(gòu)體類型的嵌套。可將原先的結(jié)構(gòu)體模板擴充為:structcredate{/*聲明結(jié)構(gòu)體類型structcredate*//*年*/intyear;intmonth;intday;/*月*//*日*/};structranking_list{intNo;charsong_name[32];charname[16];structcredatedate;intnum;/*歌曲編號*//*歌曲名稱*//*歌手姓名*//*創(chuàng)作日期,此處嵌套一層結(jié)構(gòu)體*//*票數(shù)*/11};7.1結(jié)構(gòu)體類型結(jié)構(gòu)體類型structranking_list中的成員date被定義成另一個結(jié)構(gòu)體structcredate,用于表示創(chuàng)作日期,而structcredate又包含了3個成員:year、month和day。結(jié)構(gòu)體類型的嵌套使得成員數(shù)據(jù)被進一步劃分,這有利于對數(shù)據(jù)的深入分析和處理。此時,若要進行嵌套成員的引用,依然采用成員運算符(點號),并以級聯(lián)的方式一層一層地找到最低一級的成員。例如,若定義了結(jié)構(gòu)體變量rl,想要利用該變量引用歌曲的創(chuàng)作年份,應(yīng)使用“rl.date.year”。127.1結(jié)構(gòu)體類型7.1.3結(jié)構(gòu)體數(shù)組一個結(jié)構(gòu)體變量相當于表中的一行,如只能表示“歌曲排行榜”表格中的一首歌曲的記錄信息。那么,如何表示整張的“歌曲排行榜”表格呢?顯然需要定義結(jié)構(gòu)體數(shù)組。結(jié)構(gòu)體數(shù)組是結(jié)構(gòu)和數(shù)組的結(jié)合體,與普通數(shù)組不同之處在于結(jié)構(gòu)體數(shù)組中的每個元素都是結(jié)構(gòu)體類型的數(shù)據(jù)。與定義結(jié)構(gòu)體變量一樣,可以采用以下三種方式定義結(jié)構(gòu)體數(shù)組。(1)先聲明結(jié)構(gòu)體類型,后定義結(jié)構(gòu)體數(shù)組,例如:structranking_list{intNo;charsong_name[32];charname[16];intnum;/*歌曲編號*//*歌曲名稱*//*歌手姓名*//*票數(shù)*/};structranking_listrl[40];上述代碼都定義了一個有40個元素的結(jié)構(gòu)體數(shù)組(表明有40首歌曲),每個元素類型為structranking_list,該數(shù)組所占的內(nèi)存字節(jié)數(shù)為40*sizeof(structranking_list)。137.1結(jié)構(gòu)體類型(2)在聲明結(jié)構(gòu)體類型的同時定義結(jié)構(gòu)體數(shù)組,例如:structranking_list{intNo;charsong_name[32];charname[16];intnum;/*歌曲編號*//*歌曲名稱*//*歌手姓名*//*票數(shù)*/}rl[40];(3)直接定義結(jié)構(gòu)體數(shù)組,例如:struct{intNo;charsong_name[32];charname[16];intnum;/*歌曲編號*//*歌曲名稱*//*歌手姓名*//*票數(shù)*/}rl[40];147.1結(jié)構(gòu)體類型結(jié)構(gòu)體數(shù)組的初始化方式與二維數(shù)組類似,都是通過為元素賦值的方式完成的。由于結(jié)構(gòu)體數(shù)組中的每個元素都是一個結(jié)構(gòu)體變量,因此,在為每個元素賦值時需要將其成員的值依次放到一對花括號中,例如:structranking_listrl[40]={{1001,"MyOldClassmate","LaoLang",0},{1002,"ThoseFlowers","PuShu",0},{1003,"Tomorrowwillbebetter","LuoDayou",0}};和普通數(shù)組類似,此時在定義了結(jié)構(gòu)體數(shù)組rl的同時對數(shù)組的前3個元素進行了初始化,而其他元素依然被預分配了足夠的存儲空間,并自動賦為0值或空字符。對結(jié)構(gòu)體數(shù)組元素中某個成員的引用依然可以采用成員運算符(點號)。例如,要訪問第2首歌曲的歌名可以使用rl[1].song_name。157.1結(jié)構(gòu)體類型【案例7.3】現(xiàn)有3首歌曲,假設(shè)每首歌曲包含編號、歌曲名稱、歌手姓名、票數(shù)等信息,每位歌迷只能投票選其中的一首?,F(xiàn)有10位歌迷進行投票,要求編寫統(tǒng)計選票的程序,先后輸入被選歌曲的名稱,最后輸出每首歌的得票結(jié)果。由于處理的主要對象是歌曲信息,每首歌曲又包含了不同類型的數(shù)據(jù)信息,需要使用結(jié)構(gòu)體類型,又考慮到有多首歌曲參加排行,可采用結(jié)構(gòu)型數(shù)組將歌曲組織在一起。#include<stdio.h>#include<string.h>structranking_list{/*定義結(jié)構(gòu)體數(shù)組并初始化*/intNo;charsong_name[32];charname[16];intnum;/*歌曲編號*//*歌曲名稱*//*歌手姓名*//*票數(shù)*/}rl[3]={{1001,"MyOldClassmate","LaoLang",0},{1002,"ThoseFlowers","PuShu",0},{1003,Dayou",0}};"Tomorrowwillbebetter","Luo167.1結(jié)構(gòu)體類型intmain(){inti,j;chars_name[32];printf("請輸入你喜愛的歌曲名稱:\n");for(i=1;i<=10;i++){gets(s_name);/*請思考:歌迷輸入喜愛的歌曲名,此處能否使用語句“scanf("%s",s_name);”?*/for(j=0;j<3;j++)if(strcmp(s_name,rl[j].song_name)==0)/*請注意,字符串比較需使用函數(shù)strcmp*/rl[j].num++;/*若歌名匹配,則投票成功,該首歌曲的票數(shù)增加*/}printf("投票結(jié)果:\n");for(i=0;i<3;i++)printf("%5s:%d\n",rl[i].song_name,rl[i].num);/*輸出歌名和該首歌的票數(shù)*/return0;}177.1結(jié)構(gòu)體類型7.1.4結(jié)構(gòu)體與指針之前介紹過的指針指向的是基本數(shù)據(jù)類型、數(shù)組、函數(shù)等,實際上指針還可以指向結(jié)構(gòu)體,從而成為“結(jié)構(gòu)體指針變量”。結(jié)構(gòu)體指針變量的定義方式與一般指針變量類似,例如:structranking_listrl={1001,"MyOldClassmate","LaoLang",0};structranking_list*p=&rl;此時,定義了一個structranking_list類型的指針p,并通過“&”將結(jié)構(gòu)體變量rl的地址賦值給p,此時的p指向的是結(jié)構(gòu)體變量rl所占內(nèi)存空間的首地址,即p是指向結(jié)構(gòu)體變量rl的指針。在程序中定義了一個結(jié)構(gòu)體指針變量后,就可以通過“指針名->成員名”的方式來訪問結(jié)構(gòu)體變量中的成員,其中“->”稱為指向運算符。在指向運算符中,注意“-”和“>”之間不能有空格。如果要引用排行榜中的“票數(shù)”成員num,可使用語句:p->num;當然,它等價于:rl.num;也等價于:(*p).num;此處是先利用(*p)取出指針變量p所指向的結(jié)構(gòu)體的內(nèi)容,再將其看成一個結(jié)構(gòu)體變量,并利用成員(分量)運算符訪問它的成員。請注意,(*p)的括號不可省略,否則會出現(xiàn)運算優(yōu)先級的錯誤。187.1結(jié)構(gòu)體類型如果結(jié)構(gòu)體中存在嵌套,比如要引用歌曲排行榜”表格中,“創(chuàng)作日期”中的“月”,可使用語句:p->date.month;如果是結(jié)構(gòu)體數(shù)組,如“structranking_listrl[40];”,那么,可以定義一個結(jié)構(gòu)體變量指向該數(shù)組,其定義方法為:structranking_list*p=rl;等價于:structranking_list*p=&rl[0];這是由于數(shù)組名就是表示數(shù)組首地址的常量,因此指針p指向結(jié)構(gòu)體數(shù)組rl,也就可以表示為將數(shù)組首元素的地址賦給p。此時,也可以使用指向運算符->進行引用,如此時要引用第1首歌的“票數(shù)”成員num,可使用語句:p->num;如果進行了p+1,則指向是下一個結(jié)構(gòu)體數(shù)組元素(第2首歌)的首地址,以此類推。請注意區(qū)分(++p)->num與(p++)->num:前者是先使p加1,然后引用p所指向的元素的num值(即第2首歌的票數(shù));后者是先引用出p->num的值(即第1首歌的票數(shù)),然后再使p自增,最終p指向第2首歌(即rl[1])的首地址。197.1結(jié)構(gòu)體類型注意:結(jié)構(gòu)體數(shù)組指針p是一個指向structranking_list類型對象的指針,它不能用來指向數(shù)組元素中的某個成員,也就是說不允許將某個成員的地址賦值給它。例如:p=&rl[1].num;/*錯誤,由于“rl[1].num”是數(shù)組rl[1]中成員num的首地址*/因此,不要認為只要p是指針,就可以存放任意的地址。如果要將結(jié)構(gòu)體數(shù)組中某個成員的地址賦給p,需要使用強制類型轉(zhuǎn)換,先將成員的地址轉(zhuǎn)換成p的類型,例如:p=(structranking_list*)(&rl[1].num);此時,p的值是rl[1]元素的num成員的起始地址,可以使用語句“printf("%d",*p);”輸出rl[1]中成員num的值。207.1結(jié)構(gòu)體類型7.1.5結(jié)構(gòu)體與函數(shù)函數(shù)間不僅可以傳遞簡單變量、數(shù)組、指針等類型,還可以傳遞結(jié)構(gòu)體類型的數(shù)據(jù)。結(jié)構(gòu)體類型的參數(shù)傳遞具體可分為以下三種情況。(1)向函數(shù)傳遞結(jié)構(gòu)體的單個成員:這種情況和普通變量作為函數(shù)參數(shù)類似,都是復制單個成員的內(nèi)容,是一種按數(shù)值的傳遞,在函數(shù)體內(nèi)的任何操作都不會影響到原結(jié)構(gòu)體成員的值。(2)利用結(jié)構(gòu)體變量作為函數(shù)參數(shù),向函數(shù)傳遞結(jié)構(gòu)體的完整結(jié)構(gòu):這種方式要確保實參和形參都必須是同一種結(jié)構(gòu)體類型,傳遞時將結(jié)構(gòu)體的所有內(nèi)容(多個值)復制到被調(diào)函數(shù)中,也是一種按數(shù)值的傳遞,在函數(shù)體內(nèi)對形參結(jié)構(gòu)體成員的任何操作依然不會影響到原結(jié)構(gòu)體成員的值。217.1結(jié)構(gòu)體類型【案例7.3改造1】利用結(jié)構(gòu)體變量作為函數(shù)參數(shù)。#include<stdio.h>#include<string.h>structranking_list{/*定義結(jié)構(gòu)體類型*/intNo;charsong_name[32];charname[16];intnum;/*歌曲編號*//*歌曲名稱*//*歌手姓名*//*票數(shù)*/};voidprintInfo(structranking_listr){printf("song_name:printf("num:%s\n",r.song_name);%d\n",r.num);}intmain(){structranking_listrl={1001,"MyOldClassmate","LaoLang",1889};printInfo(rl);/*結(jié)構(gòu)體變量rl作為函數(shù)參數(shù)*/return0;}227.1結(jié)構(gòu)體類型本案例定義了一個用于輸出歌曲名稱和票數(shù)這兩個結(jié)構(gòu)體數(shù)據(jù)的printfInfo()函數(shù),該函數(shù)接收了結(jié)構(gòu)體類型的參數(shù)變量rl。不難看出,當將結(jié)構(gòu)體變量作為參數(shù)傳遞給函數(shù)時,其傳參的方式與普通變量也非常相似。利用結(jié)構(gòu)體變量作為函數(shù)參數(shù),由于被調(diào)函數(shù)的形參也是結(jié)構(gòu)體類型,所以也要按該結(jié)構(gòu)體類型所占用的內(nèi)存大小為其分配一定的存儲空間,時空開銷較大。(3)使用結(jié)構(gòu)體數(shù)組名或指向結(jié)構(gòu)體的指針作函數(shù)參數(shù):這種方式的本質(zhì)是向函數(shù)傳遞結(jié)構(gòu)體的首地址,因此在函數(shù)中對結(jié)構(gòu)體的任何操作都會直接影響到結(jié)構(gòu)體成員的值。由于此種傳遞不需要開辟空間進行多個值的復制,所以相對于上述第(2)種情況而言,時空效率更高。237.1結(jié)構(gòu)體類型【案例7.3改造2】在【案例7.3】的基礎(chǔ)上要求投票人數(shù)從鍵盤輸入,并利用結(jié)構(gòu)體數(shù)組作為函數(shù)參數(shù)。#include<#include<stdiostring.h.>h>struct{intranking_listNo;/*/*歌曲編號定義結(jié)構(gòu)體數(shù)組并初始化*/*//*/*歌曲名稱歌手姓名*/*/charcharsong_namename[16];[32];}rl[3int]=num{{1001;,{"Luo1002Dayou",,"Those0}}Flowers","Pu;Shu",0},{1003,void{vote(structranking_listr[],intn)"MyOldClassmate","Lao/*票數(shù)*/Lang",0},"Tomorrowwillbebetter",intcharis_name,j;[40];{for(i=1;i<=n;i++)getsfor(j=(s_name0;j<3);;j++)if(strcmprl[j](s_name,r.num++;[j].song_name)==0)}int{intmain()i;}printfscanf("("Input%d\n",&thei);numberofvoter:");printfvote(rl(",\nThei);resultis:\n");/*使用結(jié)構(gòu)體數(shù)組名rl作為函數(shù)參數(shù)*/for(i=printf0;i<3(";%5is++:%)d\n",rl[i].song_name,return0;rl[i].num);}247.1結(jié)構(gòu)體類型【案例7.3改造3】實現(xiàn)歌曲排行榜的菜單化信息管理,要求提供輸入歌曲信息、瀏覽歌曲信息、投票和輸出前10名(Top10)等功能。分析:為方便用戶使用,可設(shè)計一個菜單,其中列出了程序提供的各項操作功能,用戶可以自由地選擇其中的某項執(zhí)行。按照結(jié)構(gòu)化程序設(shè)計的理念,可以將解決該問題的一系列操作分解成以下若干個函數(shù)模塊。displayMenu():顯示菜單choiceItem():選擇菜單input():輸入歌曲信息browse():瀏覽歌曲信息top10():顯示前10名歌曲信息vote():投票257.1結(jié)構(gòu)體類型顯然,這7個模塊可以分別使用7個函數(shù)實現(xiàn),除main()函數(shù)外,其余函數(shù)的原型可設(shè)計為:(1)voiddisplayMenu():該函數(shù)負責顯示菜單的內(nèi)容。(2)intchoiceItem():該函數(shù)用于返回用戶的操作序號。(3)intinput(structranking_lists[]):該函數(shù)完成歌曲信息的輸入。輸入的歌曲信息保存在形式參數(shù)指定的結(jié)構(gòu)體數(shù)組中,函數(shù)返回參加排行榜的歌曲數(shù)量。(4)voidbrowse(structranking_lists[],intn):該函數(shù)用于對歌曲信息的瀏覽。(5)voidTop10(structranking_lists[],intn):該函數(shù)用于顯示前10名的歌曲信息。(6)voidvote(structranking_lists[],intn):該函數(shù)用于進行投票操作。函數(shù)(4)、(5)、(6)的形式參數(shù)表完全一樣,其中,n是參加排行的歌曲數(shù)量,s存放著所有參加排行榜的歌曲信息。主程序為所有歌曲信息的保存提供了結(jié)構(gòu)體數(shù)組,各個模塊通過函數(shù)參數(shù)使用該數(shù)組;信息輸入時得到歌曲數(shù)量,各個模塊的功能實現(xiàn)中需要引用該數(shù)量。除此之外,沒有其他共享的數(shù)據(jù)信息。267.2鏈表7.2.1鏈表的概念現(xiàn)有連續(xù)排列的英文字母表A,B,C,D,E,…可以使用數(shù)組來存儲。如果要處理的對象更加復雜,比如是一組批量的學生數(shù)據(jù),可以使用結(jié)構(gòu)體數(shù)組進行處理。但是,數(shù)組不是萬能的,作為順序存儲的優(yōu)秀代表,數(shù)組的最大優(yōu)點是可以利用索引下標快速隨機地對數(shù)據(jù)進行訪問。但是,它也有致命的缺點,主要體現(xiàn)在以下方面。(1)數(shù)組一旦定義,其大小不可變化,缺乏使用的彈性。例如,定義了chara[10];就只能存儲10個字母,如果有更多的字母需要存儲,就只能重新定義更大的數(shù)組。(2)使用數(shù)組后,對數(shù)據(jù)的插入和刪除等操作的效率低下。比如要組織順序排列的字母表時,發(fā)現(xiàn)遺漏了字母“B”,這就要求在字母“A”和字母“C”間進行插入,根據(jù)數(shù)組的相關(guān)知識不難得出,從字母“C”開始后面所有的字母都要逐個進行后移,為字母“B”騰出插入的位置,這就需要移動很多的元素。如果進行字母的刪除,也是類似的做法,相應(yīng)的字母都要逐個進行前移,這種移動要通過很多的賦值操作來完成,程序的時空效率不高。那么,是否存在一種新的存儲方法能避免數(shù)組的這兩大缺陷呢?答案是肯定的,這就需要用到本節(jié)將要介紹的鏈表。277.2鏈表如果說數(shù)組采用的是順序存儲結(jié)構(gòu),那么鏈表,顧名思義采用的是一種鏈式存儲結(jié)構(gòu)。在鏈表結(jié)構(gòu)中,每個元素稱為一個節(jié)點(Node),該節(jié)點不單是簡單存儲一個值,還需要存儲下一個元素(或稱為“后繼節(jié)點”)的地址位置。也就是說,每個節(jié)點是由兩個部分構(gòu)成的:前一部分是數(shù)據(jù)域,用于存儲該節(jié)點的本身的數(shù)值,一般使用data表示;另一部分是指針域,用于存儲后繼節(jié)點的位置信息,因此它是指向后繼節(jié)點的指針,一般使用next表示,利用這樣的指針,一個個節(jié)點就可以掛接起來,形成如下圖所示的形態(tài),這是最簡單的一種鏈表,稱為單向鏈表,也是本節(jié)關(guān)注的重點。單向鏈表好比現(xiàn)實生活中的火車,數(shù)據(jù)域data相當于是一節(jié)節(jié)載貨的車廂,指針域next相當于一個個牽引的掛鉤,二者共同作用、相互配合,便形成了一列完整的火車。287.2鏈表由于鏈表中各節(jié)點的地址都記錄在前一個節(jié)點(或稱為“前驅(qū)節(jié)點”)的指針域中,這就使得各節(jié)點的存儲單元可以是連續(xù)的,更可以是不連續(xù)的,這就好比有很多朋友,即使他們不住在同一連續(xù)的區(qū)域,要想知道其中某個人的住址也很簡單,只需通過詢問前一個朋友便可知曉。由此不難看出,鏈表可以使用任意的存儲單元存儲數(shù)據(jù),十分靈活。那么,如何使用代碼定義鏈表結(jié)構(gòu)呢?由于data域和next域表示的是兩種不同類型的成員,因此要使用結(jié)構(gòu)體:structLink{intdata;structLink*next;/*數(shù)據(jù)域*//*指針域*/};其中,數(shù)據(jù)域用于存儲數(shù)據(jù)元素信息(可以不止一種數(shù)據(jù)),指針域用于存儲后繼節(jié)點的地址信息。由于鏈表中的每個節(jié)點總是指向具有相同結(jié)構(gòu)的后繼節(jié)點,所以要用遞歸結(jié)構(gòu)的方式進行定義。297.2鏈表但是,第1個節(jié)點(首節(jié)點,或稱為頭節(jié)點)非常特殊,由于沒有節(jié)點存儲它的地址信息,因此在實際使用時,往往再定義一個指向第1個節(jié)點的指針head,稱為頭指針。相應(yīng)地,在鏈表的最后一個節(jié)點(末節(jié)點),由于其沒有后繼節(jié)點,因此它的指針域部分為空(NULL),為簡單起見,使用符號“∧”代表空指針。鏈表這種存儲結(jié)構(gòu)決定了其對數(shù)據(jù)的處理方式與數(shù)組不同,在鏈表中只能進行順序訪問,不能像數(shù)組那樣進行隨機訪問。要找到鏈表中的某個元素,首先要找到鏈表的頭指針,這樣才可以找到第1個節(jié)點,接著通過第1個節(jié)點的指針域找到第2個節(jié)點,再通過第2個節(jié)點的指針域找到第3個節(jié)點……依此類推,就像游戲“擊鼓傳花”一樣,一步步地找到目標數(shù)據(jù)節(jié)點。如果到達了某節(jié)點的指針域為NULL時,說明已經(jīng)達到了鏈表的尾部,查找失敗。由此可見,頭指針非常重要,因為它表明了鏈表的首部位置,一旦頭指針丟失,整個鏈表將會“群龍無首”,數(shù)據(jù)全部丟失。當然,如果某個節(jié)點指針域中的地址信息丟失,形成了“斷鏈”,也會造成其后繼所有節(jié)點的數(shù)據(jù)無法被訪問到,這就好比火車中某個掛鉤的斷開就會造成車廂的脫節(jié)。307.2鏈表7.2.2鏈表的基本操作包括鏈表的建立、節(jié)點的插入、節(jié)點的刪除、節(jié)點的查找、鏈表的遍歷(Traversal)等,以單向鏈表為例講解。1.單向鏈表的建立若原鏈表為空表(head==NULL),則將新建節(jié)點p置為頭節(jié)點(1)head=phead新建節(jié)點pdatan∧extpr(2)(3)pr=ppr->next=NULLp=(structlink*)malloc(sizeof(structlink));p->data=nodeData;31/657.2鏈表

若原鏈表為非空,則將新建節(jié)點p添加到表尾(1)pr->next=pheadprdata∧next新建節(jié)點pdatan∧ext(2)(3)pr=ppr->next=NULLpr可見,每一次指針p總是指向新創(chuàng)建的節(jié)點,而指針pr則留在原地與新節(jié)點建立關(guān)聯(lián)。這樣將新節(jié)點逐個從鏈表的表尾接入該表中,完成單向鏈表的創(chuàng)建。整個過程就像火車的制造,不斷地生產(chǎn)出一節(jié)節(jié)新車廂,并利用掛鉤在尾部進行掛接。由此不難看出,鏈表的建立是使用動態(tài)內(nèi)存分配機制完成的,有需要時可隨時開辟節(jié)點并建立起前后相鏈接的關(guān)系,需要多少節(jié)點就相應(yīng)地開辟多大的存儲空間,這就使得鏈表的大小可以不固定,實現(xiàn)彈性伸縮,不必像數(shù)組那樣事先預設(shè)空間大小。當然,對不需要的節(jié)點也可以即刻回收和釋放,有利于充分利用計算機內(nèi)存空間,靈活實現(xiàn)內(nèi)存的動態(tài)管理。32/657.2鏈表【案例7.4】創(chuàng)建一個存放學生信息(學號,性別,年齡)(輸入的學號是非正數(shù)作為結(jié)束標志)的鏈表,并打印輸出。本案例涉及兩部分的操作:一是建立鏈表;二是訪問并輸出鏈表中各節(jié)點數(shù)據(jù)值,也就是鏈表的遍歷操作,可以定義一個指針temp,從頭指針head開始在鏈表的節(jié)點中逐一向后移動(temp=temp->next;),直至表尾(temp->next==NULL)。337.2鏈表#include<stdlib.h>#include<stdio.h>structstudent{/*鏈表節(jié)點的結(jié)構(gòu)*/intnum;charsex;intage;structstudent*next;/*學號*//*性別,使用字符'M'代表男,'F'代表女*//*年齡*//*指針域*/};intmain(){structstudent*create(structstudent*);voidprint(structstudent*);structstudent*head=NULL;head=create(head);print(head);return0;/*函數(shù)聲明*//*函數(shù)聲明*//*定義頭指針*//*創(chuàng)建鏈表*//*打印鏈表*/}347.2鏈表structstudent*create(structstudent*head){/*返回與節(jié)點相同類型的指針*/structstudent*p,*pr;p=pr=(structstudent*)malloc(sizeof(structstudent));/*申請新節(jié)點*/scanf("%d,%c,%d",&p->num,&p->sex,&p->age);/*輸入節(jié)點的值*/p->next=NULL;while(p->num>0){/*將新節(jié)點的指針置為空*//*輸入節(jié)點的數(shù)值大于0*/if(head==NULL)else{head=p;/*若是空表,接入表頭*/pr->next=p;pr=p;/*若是非空表,接到表尾*/}p=(structstudent*)malloc(sizeof(structstudent));/*申請下一個新節(jié)點*/scanf("%d,%c,%d",&p->num,&p->sex,&p->age);/*輸入節(jié)點的值*/p->next=NULL;}returnhead;/*返回鏈表的頭指針*/}357.2鏈表voidprint(structstudent*head)/*輸出以head為頭的鏈表各節(jié)點的值*/{structstudent*temp;printf("\n");temp=head;if(head!=NULL){/*取得鏈表的頭指針*/while(temp!=NULL){printf("%d,%c,%d\n",temp->num,temp->sex,temp->age);temp=temp->next;}}}367.2鏈表在建立鏈表時,如果想將鏈表節(jié)點的數(shù)據(jù)內(nèi)容與輸入的順序相反,就需要采用頭插法,每次將新產(chǎn)生的節(jié)點接入表頭,此時不需要尾部指針pr,核心代碼變化成:p->next=head;head=p;/*指針p指向新節(jié)點,head為頭指針*/2.單向鏈表節(jié)點的刪除

若原鏈表為空表,則退出程序

若待刪除節(jié)點p是頭節(jié)點,則將head指向當前節(jié)點的下一個節(jié)點即可刪除當前節(jié)點(1)head=p->next(2)free(p)head待刪除節(jié)點pdatanextdatanext頭節(jié)點377.2鏈表

若待刪除節(jié)點不是頭節(jié)點,則將前一節(jié)點的指針域指向當前節(jié)點的下一節(jié)點。(1)pr->next=p->next(2)free(p)datanext待刪除節(jié)點prdatanextpdatanextdatanext中間節(jié)點

若已搜索到表尾(p->next==NULL)仍未找到待刪除節(jié)點,則顯示“未找到”。7.2鏈表【案例7.4改造1】將【案例7.4】增加刪除功能,實現(xiàn)對相應(yīng)學生信息的刪除。structstudent*del(structstudent*head,intnum)/*num為待查的學生學號*/{structstudent*pr,*p;if(head==NULL)printf("\nListisnull.\n");/*若是空表則無法刪除節(jié)點*/p=head;while(num!=p->num&&p->next!=NULL){/*尋找待刪除的節(jié)點*/pr=p;p=p->next;}if(num==p->num){/*找到待刪除的節(jié)點*/if(p==head)elsefree(p);printf("%dhasbeendeleted.\n",num);head=p->next;/*待刪除的是首節(jié)點*//*待刪除的不是首節(jié)點*/pr->next=p->next;}elseprintf("%dcannotbefound!\n",num);returnhead;39}7.2鏈表很顯然,要進行節(jié)點的刪除,需要先找到待刪除的節(jié)點位置,因此在本案例中,可看到在鏈表中如何進行節(jié)點的查找:定義指針p,初始為頭指針的位置,只要數(shù)據(jù)未找到(num!=p->num)且p未移動到表尾(p->next!=NULL),p都要不斷向后移動(p=p->next)。此外,還定義了另一個指針pr,它在移動的過程中總是緊隨著指針p,目的是定位出待刪除節(jié)點的前一個位置,從而更好地實現(xiàn)節(jié)點的刪除。407.2鏈表3.單向鏈表節(jié)點的插入

若原鏈表為空表,則將新節(jié)點p作為頭節(jié)點,讓head指向新節(jié)點p。(1)head=phead待插入節(jié)點pdata∧(2)p->next=NULL417.2鏈表

若原鏈表為非空,則按節(jié)點值的大?。僭O(shè)已升序排列)確定插入新節(jié)點的位置。(a)若在頭節(jié)點前插入節(jié)點,則將新節(jié)點的指針域指向原鏈表的頭節(jié)點,且讓head指向新節(jié)點。head(1)p->next=head(2)head=pdatanext待插入節(jié)點pdatanextdatanextdata∧7.2鏈表(b)若在鏈表中間插入新節(jié)點,則將新節(jié)點的指針域指向下一節(jié)點且讓前一節(jié)點的指針域指向新節(jié)點。(1)p->next=pr->next(2)pr->next=pprdatanextdatanext待插入節(jié)點pdatanextdatanextdata∧注意:在節(jié)點插入時,要做到“先連后斷”,也就是說,語句“p->next=pr->next;”和語句“pr->next=p;”的順序不能對調(diào)。如果先執(zhí)行了“pr->next=p;”那么會造成插入點后的那個節(jié)點的地址沒有被任何節(jié)點的指針域記錄,使得插入點后的節(jié)點斷鏈,節(jié)點數(shù)據(jù)丟失,無法被找回。7.2鏈表

若在表尾插入新節(jié)點,則末節(jié)點指針域指向新節(jié)點。datanext原末節(jié)點prdata∧next待插入節(jié)點pdatan∧ext(1)pr->next=p(2)p->next=NULL7.2鏈表【案例7.4改造2】將【案例7.4】增加插入功能,實現(xiàn)對相應(yīng)學生信息的插入操作。假設(shè)學生信息鏈表已按學號(num)升序排列,現(xiàn)需要對相應(yīng)學生信息進行新增操作,要求依然保持原鏈表的升序狀態(tài)。structstudent*insert(structstudent*head,structstudent*stud){/*stud為新增的學生節(jié)點*/structstudent*pr,pr=head;p=stud;if(head==NULL){*p,*temp;/*需要三個輔助指針*//*若空表,讓新節(jié)點成為鏈表的首節(jié)點*/head=p;p->next=NULL;}else{while((pr->num<p->num)&&(pr->next!=NULL))/*找到插入點*/{temp=pr;pr=pr->next;}457.2鏈表if(pr->num>=p->num){if(pr==head){/*在首節(jié)點前插入新節(jié)點*/p->next=head;head=p;}else{/*在鏈表中插入新節(jié)點*/pr=temp;p->next=pr->next;pr->next=p;}}else{pr->next=p;/*在表尾插入新節(jié)點*/}}returnhead;}467.2鏈表綜上所述,鏈表可以動態(tài)定義存儲大小,實現(xiàn)內(nèi)存的彈性動態(tài)管理,并能很方便地進行數(shù)據(jù)的插入和刪除,因此在現(xiàn)實生活中,鏈表有著廣泛的應(yīng)用,常用的Excel表格、各種數(shù)據(jù)庫中的表(學生成績表、人事信息表等)都是采用鏈式存儲結(jié)構(gòu)存儲的,以便于插入、刪除數(shù)據(jù)。但是,與數(shù)組這樣的順序存儲結(jié)構(gòu)相比,鏈式存儲技術(shù)還是存在一些缺陷,主要體現(xiàn)在兩個方面。(1)無法實現(xiàn)隨機快速查找:要查找某個節(jié)點,只能從鏈表的表頭開始,導致折半查找等技術(shù)無法使用。(2)較容易出現(xiàn)斷鏈。一旦由于某種原因?qū)е骆湵碇心骋粋€鏈丟失,即節(jié)點的指針不再指向下一個節(jié)點,該節(jié)點后的所有節(jié)點將全部丟失,無法找回。鏈表都是一種比較實用且十分重要的數(shù)據(jù)結(jié)構(gòu)。除了單向鏈表,還有雙向鏈表、循環(huán)鏈表等形式,有興趣可自行學習掌握。477.3共用體類型“婚姻狀況”部分,一個人的婚姻狀況一般有三種狀態(tài):未婚、已婚、離婚,且在某一時刻只能有一種狀態(tài)存在(如張三目前是已婚狀態(tài)),因此不能定義成之前學過的結(jié)構(gòu)體。在C語言中,“共用體”這種自定義數(shù)據(jù)類型可以解決這樣的問題。共用體又叫聯(lián)合體,它允許多個成員共同使用同一塊內(nèi)存,常用于存儲程序中邏輯相關(guān)但情形相斥的變量成員。由于其具有“共用”的特征,因而靈活使用這種類型可以節(jié)省內(nèi)存空間。487.3共用體類型7.3.1共用體類型的定義共用體類型的定義格式為:union共用體類型名{數(shù)據(jù)類型數(shù)據(jù)類型…數(shù)據(jù)類型成員名1;成員名2;成員名n;};其中,“union”是定義共用體類型的關(guān)鍵字,其后是“共用體類型名”,在“共用體類型名”后的花括號中定義了共用體類型的成員項,每個成員是由“數(shù)據(jù)類型”和“成員名”共同組成的。例如:uniondata{shortx;floaty;charz;};定義了一個名為data的共用體類型,該類型由三個不同類型的成員組成,這些成員共享同一塊存儲空間。497.3共用體類型7.3.2共用體變量的定義假如要定義兩個data類型的共用體變量a和b,則可以采用以下三種方式。(1)先定義共用體類型,再定義共用體變量:uniondata{shortx;floaty;charz;};uniondataa,b;(2)在定義共用體類型的同時定義共用體變量:uniondata{shortx;floaty;charz;}a,b;(3)直接定義共用體類型變量:union{shortx;floaty;charz;50}a,b;7.3共用體類型以共用體a為例,它由三個成員組成,分別是x、y和z。由于共用體中不同的成員共同占用一段內(nèi)存空間,編譯時系統(tǒng)會按照占內(nèi)存最大的成員長度為a分配內(nèi)存,由于成員y的長度最長,它占4字節(jié),所以共用體變量a的內(nèi)存空間也為4字節(jié)。共用體的內(nèi)存大小必須是最寬基本數(shù)據(jù)類型的整數(shù)倍,如果不是,則填充字節(jié)。union{intfloatm;x;charcharcname[;5];}a;共用體變量a的內(nèi)存大小按最大數(shù)據(jù)類型charname[5]來分配,charname[5]占5字節(jié)。此外,共用體變量a的內(nèi)存大小還必須是最寬基本數(shù)據(jù)類型的整數(shù)倍,所以填充3字節(jié),共8字節(jié)。當然,如果在使用過程中不確定共用體變量的內(nèi)存大小,還可以通過sizeof()函數(shù)求得。517.3共用體類型7.3.3共用體變量的初始化和引用由于共用體中不同類型的變量存放到同一首地址的內(nèi)存單元中,這就使得各個成員間互相覆蓋。例如,若對變量x賦值,變量y就失去了自身的意義,接下來再對變量z賦值,則x的內(nèi)容又被改變,因此在每一瞬間起作用的成員是最后一次賦值的成員。也就是說,在程序執(zhí)行的任何特定時刻,僅有一個成員駐留在共用體變量所占用的內(nèi)存空間中,在同一時刻只有一個成員是有意義的。因此,在定義共用體變量的同時,只能對其中一個成員的類型值進行初始化,共用體變量初始化的方式是:union共用體類型名共用體變量={某一個成員的類型值};請注意,盡量只有一個初始值,此處的花括號也不可省略。例如,可以使用語句:uniondataa={3};對data類型的共用體變量a中的第1個成員(成員x)進行初始化。當然,也可以對指定的某一個成員進行初始化:uniondataa={.z='k’};對data類型的共用體變量a中的成員z進行初始化。完成了共用體變量的初始化后,就可以引用共用體中的成員了,共用體變量的引用方式與結(jié)構(gòu)體類似。527.3共用體類型例如,定義共用體變量a和一個共用體指針p:uniondata{shortx;shorty;floatz;chart;};uniondataa,*p=&a;如果要引用共用體變量中的成員z,則可以使用:a.z;/*引用共用體變量a中的成員z*/或者p->z/*引用共用體指針變量p所指向的變量成員z*/由于共用體a中包含了兩個short類型的變量x和y,所以x和y的首地址與長度都一致,如果執(zhí)行了賦值語句“a.x=3;”則a.y的值也是3,對x的賦值就相當于對y賦值,反之亦然。537.3共用體類型回到前面的表格,其中的“婚姻狀況”是一種典型的邏輯相關(guān)但情形相斥的場景,自然可以定義成共用體,命名為maritalState,內(nèi)有3個成員:未婚、已婚和離婚。如果是“未婚”,則最為簡單,只要一個普通變量就可以表示;如果是“已婚”,需要進一步了解結(jié)婚日期、配偶姓名、子女數(shù)量等信息,這些不同類型的信息自然可以集結(jié)成已婚類型的結(jié)構(gòu)體;如果是“離婚”,需要進一步了解離婚日期和子女數(shù)量等信息,可以集結(jié)成離婚類型的結(jié)構(gòu)體:unionmaritalState{intsingle;structmarriedStatemarried;structdivorceStatedivorce;/*未婚*//*已婚*//*離婚*/};上述對共同體maritalState的定義,采用了在共同體中嵌套進一層結(jié)構(gòu)體的方式。進一步思考,如何控制在某個瞬間由哪個成員起作用呢?這就需要增加一個標志位變量(不妨命名為marryFlag),表明某人當前是哪種婚姻狀況,類似于前表中的“婚姻狀況標記”:當marryFlag為1時,說明此人未婚,內(nèi)存中的數(shù)據(jù)將被解釋為未婚相關(guān)的數(shù)據(jù),共用體maritalState中的single變量起作用。同理,當marryFlag為2時,說明此人是已婚狀態(tài),結(jié)構(gòu)體成員married起作用;當marryFlag為3時,說明此人是離婚狀態(tài),結(jié)構(gòu)體成員divorce起作用。547.3共用體類型最后,在總體上可設(shè)計結(jié)構(gòu)體person,完成職工個人基本信息的定義:structperson{charname[20];charsex;intage;unionmaritalStatemarital;intmarryFlag;/*姓名*//*性別*//*年齡*//*婚姻狀況*//*婚姻狀況標記*/};因此,在實際開發(fā)中,當需要存儲與表示程序中邏輯相關(guān)但情形相斥的變量成員,可利用共用體類型。該類型使幾種不同類型的變量存放到同一段內(nèi)存單元中,也就是使用覆蓋技術(shù)使得若干不同的變量共同占用一段內(nèi)存空間,以達到節(jié)省內(nèi)存空間的目的。557.4枚舉類型在程序中,經(jīng)常遇到需要使用一個整型常數(shù)來代表某一種狀態(tài)的情形,比如用0表示開關(guān)的開啟,1表示開關(guān)的關(guān)閉,但這種處理方法不直觀,易讀性差。如果我們能夠事先考慮到某一變量可能取的值,并盡量用自然語言中含義清楚的單詞來表示它的每一個狀態(tài)值,程序就很容易閱讀和理解,這便形成了枚舉(enumeration)方法,用這種方法定義的類型就是枚舉類型。再看下面的預處理命令:#defineMON#defineTUE#defineWED#defineTHU#defineFRI#defineSAT#defineSUN1234567這是利用#define指令完成對從星期一到星期天的宏定義。在C語言中,我們也可以定義枚舉類型來完成同樣的工作。567.4枚舉類型枚舉類型的定義格式是:enum[枚舉類型名]{枚舉常量列表};enumDAY{MON,TUE,WED,THU,FRI,SAT,SUN};此處,定義了enumDAY這種枚舉類型,枚舉元素為花括號中的MON、TUE、WED、THU、FRI、SAT、SUN這7個常量構(gòu)成的集合,而且僅限于這7個元素。枚舉元素的命名一般以簡單直觀、見名知義為好。需要注意的是,枚舉元素不是字符串,不需要加雙引號。在枚舉定義中,每個枚舉元素都代表一個整數(shù),C語言在編譯時按定義的順序默認它們的值分別是0,1,2,3,…(這和數(shù)組下標非常相似,是從0開始的)。但是,也可以人為顯式地指定枚舉元素的值。如上述預處理命令可以等價地使用枚舉方法寫成:enumDAY{MON=1,TUE,WED,THU,FRI,SAT,SUN};在此處,MON被定義為整數(shù)1,那么THU就是2,WED就是3,以此類推。注意:枚舉元素值是常量,不是變量,因此不能在程序中使用賦值語句進行二次賦值,如語句“MON=2;”是錯誤的。577.4枚舉類型枚舉元素間可以進行大小比較,比較時按照初始化時指定的整數(shù)來進行。如果沒有人為地指定則按默認規(guī)則處理,即第1個枚舉元素的值為0,因此MON<TUE。定義了枚舉類型enumDAY后,就可以進一步定義枚舉變量workday了,方法與結(jié)構(gòu)體、共同體類似:enumDAYworkday;或者將枚舉類型和枚舉變量的定義一次性地寫成:enumDAY{MON=1,TUE,WED,THU,FRI,SAT,SUN}workday;此時,workday只能接收枚舉元素的值,例如:workday=MON;等效于workday=1;由于枚舉變量的值是

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論