版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
第8章結(jié)構(gòu)體與共用體8.1概述8.2結(jié)構(gòu)體變量定義8.3結(jié)構(gòu)體數(shù)組8.4指向結(jié)構(gòu)體類型數(shù)據(jù)的指針8.5用指針處理鏈表8.6共用體8.7枚舉類型8.8用typedef定義類型習(xí)題
為了方便處理多個(gè)數(shù)據(jù)項(xiàng)的數(shù)據(jù),常常把這些關(guān)系密切但類型不同的數(shù)據(jù)項(xiàng)組織在一起,即“封裝”起來(lái),并為其取一個(gè)名字,在C語(yǔ)言中,就稱其為結(jié)構(gòu)體(有些高級(jí)語(yǔ)言稱之為記錄)。所以,結(jié)構(gòu)體通常是由不同數(shù)據(jù)類型的數(shù)據(jù)項(xiàng)組成,一般也稱是由不同成員組成,因此可以說(shuō):一個(gè)結(jié)構(gòu)體可包含若干成員,每一個(gè)成員可具有不同的名字及數(shù)據(jù)類型。結(jié)構(gòu)體的引入為處理復(fù)雜的數(shù)據(jù)結(jié)構(gòu)提供了有力的手段,也為函數(shù)間傳遞一組不同數(shù)據(jù)類型的數(shù)據(jù)提供了方便,特別是對(duì)于數(shù)據(jù)結(jié)構(gòu)較為復(fù)雜的大型程序提供了方便。8.1結(jié)構(gòu)體概述8.1.1結(jié)構(gòu)體的引入
現(xiàn)在請(qǐng)思考這樣一個(gè)問(wèn)題,需要用C語(yǔ)言編寫一個(gè)程序?qū)W(xué)生信息進(jìn)行處理,每個(gè)學(xué)生的基本情況由這樣一些數(shù)據(jù)項(xiàng)組成:學(xué)號(hào)(num)、姓名(name)、科目1(score[0])、科目2(score[1])、科目3(score[2])等,這些數(shù)據(jù)項(xiàng)的屬性不同,見圖8-1。8.1.1結(jié)構(gòu)體的引入設(shè)計(jì)這個(gè)程序的第一個(gè)步驟就是選擇一個(gè)表示學(xué)生信息數(shù)據(jù)的類型。這種情況下用我們之前學(xué)過(guò)的基本類型(如整型、實(shí)型、字符型變量等)或數(shù)組是不方便的。程序中要用到圖8.1所表示的數(shù)據(jù)結(jié)構(gòu),但是C語(yǔ)言沒(méi)有提供這種現(xiàn)成的數(shù)據(jù)類型,因此用戶必須要在程序中建立所需的結(jié)構(gòu)體類型。例如:#defineMAX_LEN10#defineCOURSE_NUM3structstudent /*定義學(xué)生的結(jié)構(gòu)體類型*/{ longnum; charname[MAX_LEN]; floatscore[COURSE_NUM];};8.1.1結(jié)構(gòu)體的引入8.1.2結(jié)構(gòu)體類型的定義
結(jié)構(gòu)體類型和簡(jiǎn)單類型不同,簡(jiǎn)單類型是由系統(tǒng)預(yù)定義的,如int、float、char,直接可以使用。而結(jié)構(gòu)體類型是根據(jù)需要由程序員自行定義,因此在使用之前必須先定義結(jié)構(gòu)體類型。結(jié)構(gòu)體類型定義的一般形式為:struct結(jié)構(gòu)體名
{
類型標(biāo)識(shí)符成員名; 類型標(biāo)識(shí)符成員名;
……………. };其中,struct是聲明結(jié)構(gòu)體類型時(shí)所必須使用的關(guān)鍵字,不能省略。“結(jié)構(gòu)體名”用作結(jié)構(gòu)體類型的標(biāo)志,它又稱“結(jié)構(gòu)體標(biāo)記”(structuretag),可省略。成員類型可以是基本型或構(gòu)造型。說(shuō)明:(1)
定義一個(gè)結(jié)構(gòu)體類型只是描述了此結(jié)構(gòu)體的組織形式,在編譯時(shí)并不為其分配存儲(chǔ)空間,即僅描述此數(shù)據(jù)結(jié)構(gòu)的形態(tài)或者說(shuō)模型,故不能對(duì)定義的一個(gè)結(jié)構(gòu)體類型進(jìn)行賦值、存取或運(yùn)算。(2)結(jié)構(gòu)體的成員可以是簡(jiǎn)單變量、數(shù)組、指針、結(jié)構(gòu)體或公用體等。(3)結(jié)構(gòu)體類型定義可以放在函數(shù)內(nèi)部,也可以放在函數(shù)外部。若放在內(nèi)部,則只在函數(shù)內(nèi)有效;若放在外部,則從定義點(diǎn)到源文件尾之間的所有函數(shù)都有效。(4)結(jié)構(gòu)體成員的名字可以同程序中的其它變量同名,二者不會(huì)相混,系統(tǒng)會(huì)自動(dòng)識(shí)別它。8.1.2結(jié)構(gòu)體類型的定義8.2結(jié)構(gòu)體變量定義8.2.1結(jié)構(gòu)體變量的定義與初始化1.結(jié)構(gòu)體變量的定義同其它變量一樣,結(jié)構(gòu)體變量也必須先定義,然后才能引用。一個(gè)結(jié)構(gòu)體變量的定義可以有以下三種方式:(1)先定義類型再定義變量其形式:struct結(jié)構(gòu)體名變量名,…,變量名n;如上面已定義了一個(gè)結(jié)構(gòu)體類型structstudent,可以用它來(lái)定義變量。如:structstudent
student1,student2結(jié)構(gòu)體類型名結(jié)構(gòu)體變量名;定義了student1和student2為structstudent類型的變量,即它們具有structstudent類型的結(jié)構(gòu)。如表8-1所示。在定義了結(jié)構(gòu)體變量后,系統(tǒng)會(huì)為之分配內(nèi)存單元。例如studen1和student2在內(nèi)存中各占26個(gè)字節(jié)(4+10+4+4+4=26)。應(yīng)當(dāng)注意,將一個(gè)變量定義為標(biāo)準(zhǔn)類型(基本數(shù)據(jù)類型)與定義為結(jié)構(gòu)體類型不同之處在于后者不僅要求指定變量為結(jié)構(gòu)體類型(不可以只用struct定義),而且要求指定為某一特定的結(jié)構(gòu)體類型(例如structstudent類型)。因?yàn)榭梢远x出許許多多種具體的結(jié)構(gòu)體類型。而在定義變量為整型時(shí),只需指定為int型即可。在結(jié)構(gòu)體變量的聲明中,structstudent所起的作用就像int或float在較簡(jiǎn)單的聲明中的作用一樣。另外,如果程序規(guī)模比較大,往往將對(duì)結(jié)構(gòu)體類型的聲明集中放到一個(gè)文件(以.h為后綴的“頭文件”)中。哪個(gè)源文件需用到此結(jié)構(gòu)體類型則可用#include命令將該頭文件包含到本文件中。這樣做便于裝配,便于修改,便于使用。8.2.1結(jié)構(gòu)體變量的定義與初始化(2)在聲明類型的同時(shí)定義變量如:#defineMAX_LEN10#defineCOURSE_NUM3structstudent /*定義學(xué)生的結(jié)構(gòu)體類型*/{ longnum; charname[MAX_LEN]; floatscore[COURSE_NUM];}student1,student2;它的作用與第一種方法相同,即定義了兩個(gè)structemployee類型的變量employee1、employee2。這種形式的定義的一般形式為struct結(jié)構(gòu)體名{類型標(biāo)識(shí)符成員名;}變量名列表;8.2.1結(jié)構(gòu)體變量的定義與初始化(3)直接定義結(jié)構(gòu)類型變量其一般形式為struct{類型標(biāo)識(shí)符成員名;}變量名列表;該形式即不出現(xiàn)結(jié)構(gòu)體名,第三種方法與第二種方法的區(qū)別在于第三種方法中省去了結(jié)構(gòu)名,而直接給出結(jié)構(gòu)變量。關(guān)于結(jié)構(gòu)體類型,有幾點(diǎn)要說(shuō)明:(1)類型與變量是不同的概念,不要混同。只能對(duì)變量賦值、存取或運(yùn)算,而不能對(duì)一個(gè)類型賦值、存取或運(yùn)算。在編譯時(shí),對(duì)類型是不分配空間的,只對(duì)變量分配空間。(2)對(duì)結(jié)構(gòu)體中的成員(即“域”),可以單獨(dú)使用,它的作用與地位相當(dāng)于普通變量。(3)成員也可以是一個(gè)結(jié)構(gòu)體變量。(4)成員名可以與程序中的變量名相同,二者不代表同一對(duì)象。例如,程序中可以另定義一個(gè)變量name,它與structstudent中的name是兩回事,互不干擾。(5)結(jié)構(gòu)體變量的定義一定要在結(jié)構(gòu)體類型定義之后或同時(shí)進(jìn)行,對(duì)尚未定義的結(jié)構(gòu)體類型,不能用它來(lái)定義結(jié)構(gòu)體變量。例如:對(duì)教師teacher的結(jié)構(gòu)體類型未作定義,則下面的變量定義structteacherteah1是錯(cuò)誤的;8.2.1結(jié)構(gòu)體變量的定義與初始化2.結(jié)構(gòu)體變量的初始化所謂結(jié)構(gòu)體變量初始化,就是在定義結(jié)構(gòu)體變量的同時(shí),對(duì)其成員變量賦初值,在賦值時(shí)應(yīng)注意按順序及類型依次為每個(gè)結(jié)構(gòu)體成員指定初始值。結(jié)構(gòu)體初始化的一般格式為:struct結(jié)構(gòu)體類型名結(jié)構(gòu)體變量={初始化值};
8.2.1結(jié)構(gòu)體變量的定義與初始化需要注意的是,如果結(jié)構(gòu)變量是全局變量或?yàn)殪o態(tài)變量,則可對(duì)它作初始化賦值。對(duì)局部或自動(dòng)結(jié)構(gòu)變量不能作初始化賦值,只能在程序中分別給結(jié)構(gòu)變量的成員單獨(dú)賦值。上例中,student1、student2均被定義為外部結(jié)構(gòu)變量,并對(duì)student1、student2作了初始化賦值說(shuō)明:(1)初始化數(shù)據(jù)之間用逗號(hào)分隔。(2)
初始化數(shù)據(jù)的個(gè)數(shù)一般與成員的個(gè)數(shù)相同,若小于成員數(shù),則剩余的成員將被自動(dòng)初始化為0(若成員是指針,則初始化為NULL)。(3)
初始化數(shù)據(jù)的類型要與相應(yīng)成員變量的類型一致。(4)初始化時(shí)只能對(duì)整個(gè)結(jié)構(gòu)體變量進(jìn)行,不能對(duì)結(jié)構(gòu)體類型中的某個(gè)成員進(jìn)行初始化賦值。8.2.1結(jié)構(gòu)體變量的定義與初始化8.2.2結(jié)構(gòu)體變量的引用
在C語(yǔ)言程序中,不準(zhǔn)許對(duì)結(jié)構(gòu)變量整體進(jìn)行各種運(yùn)算、賦值或輸入輸出操作,而只能是對(duì)其成員進(jìn)行此類操作。引用結(jié)構(gòu)體變量成員的一般形式:結(jié)構(gòu)體變量名.成員名其中"."是結(jié)構(gòu)體成員運(yùn)算符,其優(yōu)先級(jí)別最高,結(jié)合性是自左至右。由此對(duì)結(jié)構(gòu)體成員就完全可以像操作簡(jiǎn)單變量一樣操作它。如:對(duì)上例定義的結(jié)構(gòu)體變量student1或student2,可作如下的賦值操作:student1.num=1002;="Wang";student1.score[0]=89;student1.score[1]=79student1.score[2]=83;student1.birthday.year=1978;student1.birthday.mouth=10;student1.birthday.day=12;可以引用結(jié)構(gòu)體變量成員的地址,也可以引用結(jié)構(gòu)體變量的地址。如:scanf("%d",&student1.num);(輸入student1.num的值)printf("%o",&student1);(輸出student1的首地址)但不能用以下語(yǔ)句整體讀入結(jié)構(gòu)體變量,如:scanf("%d,%s,%c,%d,%f,%s",&student1);僅在以下兩種情況下可以把結(jié)構(gòu)體變量作為一個(gè)整體來(lái)訪問(wèn)(1)結(jié)構(gòu)體變量整體賦值,注意相互賦值的兩個(gè)結(jié)構(gòu)體變量必須是同一個(gè)結(jié)構(gòu)體類型。例:student1=student2;(2)結(jié)構(gòu)體變量的地址主要用于作函數(shù)參數(shù),傳遞結(jié)構(gòu)體的地址。例:printf("%x",&student1);/*輸出student1的地址*/
8.2.2結(jié)構(gòu)體變量的引用
8.3結(jié)構(gòu)體數(shù)組一個(gè)結(jié)構(gòu)體變量中可以存放一組數(shù)據(jù)(如一個(gè)員工的工號(hào)、姓名、工資等數(shù)據(jù))。如果有10個(gè)員工的數(shù)據(jù)需要參加運(yùn)算,顯然應(yīng)該用數(shù)組,這就是結(jié)構(gòu)體數(shù)組。結(jié)構(gòu)體數(shù)組與以前介紹過(guò)的數(shù)值型數(shù)組不同之處在于每個(gè)數(shù)組元素都是一個(gè)結(jié)構(gòu)體類型的數(shù)據(jù),它們都分別包括各個(gè)成員(分量)項(xiàng)。8.3.1結(jié)構(gòu)體數(shù)組的定義與初始化1.結(jié)構(gòu)體數(shù)組的定義
在定義結(jié)構(gòu)體數(shù)組時(shí)其定義方法與定義結(jié)構(gòu)體變量方法類似,也有三種形式。這里以第一種形式為例,先定義結(jié)構(gòu)再定義變量:struct
date{ int
month;
int
day;
int
year;};structstudent{ longnum; charname[MAX_LEN]; floatscore[COURSE_NUM];structdatebirthday;};structstudentstudent[3];由此就定義了一個(gè)結(jié)構(gòu)體數(shù)組student[3],它有3個(gè)元素,每個(gè)元素都是structstudent類型,各占32個(gè)字節(jié)(4+10+4+4+4+(2+2+2)=32)。2.結(jié)構(gòu)體數(shù)組的初始化結(jié)構(gòu)體數(shù)組在定義的同時(shí)可以初始化。其一般格式是在定義之后緊跟一個(gè)用花括號(hào)括起來(lái)的一組初始數(shù)據(jù),為了增強(qiáng)可讀性,最好使每一個(gè)數(shù)組元素的初始數(shù)據(jù)也用花括弧括起來(lái),以此來(lái)區(qū)分各個(gè)數(shù)組元素。對(duì)上所定義的結(jié)構(gòu)體數(shù)組student初始化如下:stuctstudent student[2]={{1001,"Zhang",84.5,86,91,10,12,1988},{1002,"Wang",89,79,83,9,22,1980}};8.3.1結(jié)構(gòu)體數(shù)組的定義與初始化說(shuō)明(1)
可以將一個(gè)結(jié)構(gòu)體數(shù)組元素賦值給同一結(jié)構(gòu)體類型數(shù)組中另一個(gè)元素,或賦給同一類型的變量。如:structstudentstudent[3],student1;現(xiàn)在就定義了一個(gè)結(jié)構(gòu)體數(shù)組student[],又定義了一個(gè)結(jié)構(gòu)體變量employee1,則下面的賦值合法。student1=student[0];student[0]=student[1];student[1]=student1;(2)
不能把結(jié)構(gòu)體數(shù)組元素作為一個(gè)整體直接進(jìn)行輸入或輸出。如printf("%d",student[0]);或scanf("%d",&student[0]),這些都是錯(cuò)誤的。只能以單個(gè)成員為對(duì)象進(jìn)行輸入輸出,如:scanf("%s",student[0].name);scanf("%ld",&student[0].num);printf("%s%ld\n",student[0].name,student[0].num);8.3.1結(jié)構(gòu)體數(shù)組的定義與初始化8.3.2結(jié)構(gòu)體數(shù)組應(yīng)用舉例下面舉一個(gè)簡(jiǎn)單的例子來(lái)說(shuō)明結(jié)構(gòu)體數(shù)組的定義和引用。【例8.1】設(shè)有10名學(xué)生,每次輸入一個(gè)學(xué)生的名字和成績(jī),要求最后輸出每個(gè)學(xué)生各門課程的總分和平均分?!境绦虼a】#include<stdio.h>#include<stdlib.h>#include<string.h>#defineMAX_LEN10#defineSTU_NUM40#defineCOURSE_NUM3structstudent /*定義學(xué)生的結(jié)構(gòu)體類型*/{ longnum; charname[MAX_LEN]; floatscore[COURSE_NUM]; floatsum; floataver;};/*從鍵盤輸入n個(gè)學(xué)生的信息*/voidReadScore(structstudentstu[],intn){ inti,j; printf("Inputstudent'sID,nameandMATH,ENGLISH,COMPUTERscore:\n"); for(i=0;i<n;i++) { scanf("%ld%s",&stu[i].num,stu[i].name); stu[i].sum=0; for(j=0;j<COURSE_NUM;j++) { scanf("%f",&stu[i].score[j]); } }}8.3.2結(jié)構(gòu)體數(shù)組應(yīng)用舉例/*計(jì)算每個(gè)學(xué)生各門課程的總分和平均分*/voidAverSumofEveryStudent(structstudentstu[],intn){ inti,j; for(i=0;i<n;i++) { stu[i].sum=0; for(j=0;j<COURSE_NUM;j++) { stu[i].sum=stu[i].sum+stu[i].score[j];//計(jì)算每個(gè)學(xué)生總分
} stu[i].aver=stu[i].sum/COURSE_NUM;//計(jì)算每個(gè)學(xué)生均分
printf("student%d:sum=%.0f,aver=%.0f\n",i+1,stu[i].sum,stu[i].aver);
}}8.3.2結(jié)構(gòu)體數(shù)組應(yīng)用舉例intmain(){ intch,sch; intn,i; structstudentstuRecord[STU_NUM];printf("Inputstudentnumber(n<%d):",STU_NUM); scanf("%d",&n); ReadScore(stuRecord,n); /*鍵盤讀入n個(gè)學(xué)生成績(jī)*/AverSumofEveryStudent(stuRecord,n);}8.3.2結(jié)構(gòu)體數(shù)組應(yīng)用舉例【運(yùn)行結(jié)果】8.3.2結(jié)構(gòu)體數(shù)組應(yīng)用舉例8.4指向結(jié)構(gòu)體類型數(shù)據(jù)的指針一個(gè)結(jié)構(gòu)體變量的指針就是該變量所占據(jù)的內(nèi)存段的起始地址。可以設(shè)一個(gè)指針變量,用來(lái)指向一個(gè)結(jié)構(gòu)體變量,此時(shí)該指針變量的值是結(jié)構(gòu)體變量的起始地址。指針變量也可以用來(lái)指向結(jié)構(gòu)體數(shù)組中的元素。8.4.1指向結(jié)構(gòu)體變量的指針下面通過(guò)一個(gè)簡(jiǎn)單例子來(lái)說(shuō)明指向結(jié)構(gòu)體變量的指針變量的應(yīng)用。【例8.2】指向結(jié)構(gòu)體變量的指針的應(yīng)用。【程序代碼】#include<stdio.h>#include<string.h>main(){ structstudent{ longnum;charname[10];floatscore[3]; };structstudentstu_1; structstudent*p;p=&stu_1;stu_1.num=14101;strcpy(stu_1.name,"LiLin");stu_1.score[0]=87;stu_1.score[1]=84.5;stu_1.score[2]=77; printf("No.:%ld\nname:%s\nscore[0]:%f\nscore[1]:%f\nscore[2]:%f\n",stu_1.num,stu _1.name,stu_1.score[0],stu_1.score[1],stu_1.score[2]);printf("No.:%ld\nname:%s\nscore[0]:%f\nscore[1]:%f\nscore[2]:%f\n",(*p).num,(*p).name,(*p).score[0],(*p).score[1],(*p).score[2]);}8.4.1指向結(jié)構(gòu)體變量的指針【運(yùn)行結(jié)果】
8.4.1指向結(jié)構(gòu)體變量的指針在主函數(shù)中聲明了structstudent類型,然后定義一個(gè)structstudent類型的變量stu-1。同時(shí)又定義一個(gè)指針變量p,它指向一個(gè)structstudent類型的數(shù)據(jù)。在函數(shù)的執(zhí)行部分將結(jié)構(gòu)體變量stu-1的起始地址賦給指針變量p,也就是使p指向stu-1(見圖8.3),然后對(duì)stu-1的各成員賦值。printf函數(shù)是輸出stu-1的各個(gè)成員的值。用stu-1.num表示stu-1中的成員num,余類推。第二個(gè)printf函數(shù)也是用來(lái)輸出stu-1各成員的值,但使用的是(*p).num這樣的形式。(*p)表示p指向的結(jié)構(gòu)體變量,(*p).num是p指向的結(jié)構(gòu)體變量中的成員num。注意*p兩側(cè)的括弧不可省,因?yàn)椤埃眱?yōu)先于“*”運(yùn)算符,*p.num就等價(jià)于*(p.num)了。8.4.1指向結(jié)構(gòu)體變量的指針可見兩個(gè)printf函數(shù)輸出的結(jié)果是相同的。在C語(yǔ)言中,為了使用方便和使之直觀,可以把(*p).num改用p—>num來(lái)代替,它表示*p所指向的結(jié)構(gòu)體變量中的num成員。同樣,(*p).name等價(jià)于p—>name。也就是說(shuō),以下三種形式等價(jià):(1)結(jié)構(gòu)體變量.成員名(2)(*p).成員名(3)p->成員名上面程序中最后一個(gè)printf函數(shù)中的輸出項(xiàng)表列可以改寫為p->num,p->name,p->sex,p->score,其中->稱為指向運(yùn)算符。請(qǐng)分析以下幾種運(yùn)算:p->n:得到p指向的結(jié)構(gòu)體變量中的成員n的值。p->n++:得到p指向的結(jié)構(gòu)體變量中的成員n的值,用完該值后使它加1。++p->n:得到p指向的結(jié)構(gòu)體變量中的成員n的值使之加1(先加)。8.4.1指向結(jié)構(gòu)體變量的指針8.4.2指向結(jié)構(gòu)體數(shù)組的指針以前已經(jīng)介紹過(guò),可以使用指向數(shù)組或數(shù)組元素的指針和指針變量。同樣,對(duì)結(jié)構(gòu)體數(shù)組及其元素也可以用指針或指針變量來(lái)指向。【例8.3】指向結(jié)構(gòu)體數(shù)組的指針的應(yīng)用。structstudent{ intnum;charname[20];charsex;intage;};structstudentstu[3]={{14101,"LiLin",'M',20},{14102,"LiHui",'M',19},{14104,"Chen Lin",'F',19}};main(){ structstudent*p; printf("No.Namesexage\n");for(p=stu;p<stu+3;p++) printf("%5d%-20s%2c%4d\n",p->num,p->name,p->sex,p->age);}【運(yùn)行結(jié)果】8.4.2指向結(jié)構(gòu)體數(shù)組的指針p是指向structstudent結(jié)構(gòu)體類型數(shù)據(jù)的指針變量。在for語(yǔ)句中先使p的初值為stu,也就是數(shù)組stu的起始地址,見圖8.4中p的指向。在第一次循環(huán)中輸出stu[0]的各個(gè)成員值。然后執(zhí)行p++,使p自加1。p加1意味著p所增加的值為結(jié)構(gòu)體數(shù)組stu的一個(gè)元素所占的字節(jié)數(shù)(在本例中為2+20+1+2=25字節(jié))。執(zhí)行p++后p的值等于stu+1,p指向stu[1]的起始地址,見圖8.4中p‘的指向。在第二次循環(huán)中輸出stu[1]的各成員值。在執(zhí)行p++后,p的值等于stu+2,它的指向見圖8.4中的″p″。再輸出stu[2]的各成員值。在執(zhí)行p++后,p的值變?yōu)閟tu+3,已不再小于stu+3了,不再執(zhí)行循環(huán)。8.4.2指向結(jié)構(gòu)體數(shù)組的指針8.4.3用結(jié)構(gòu)體變量和指向結(jié)構(gòu)體的指針作函數(shù)參數(shù)
將一個(gè)結(jié)構(gòu)體變量的值傳遞給另一個(gè)函數(shù),有3種方法:(1)用結(jié)構(gòu)體變量的成員作參數(shù)。例如,用stu[1].num或stu[2].name作函數(shù)實(shí)參,將實(shí)參值傳給形參。用法和用普通變量作實(shí)參是一樣的,屬于“值傳遞”方式。應(yīng)當(dāng)注意實(shí)參與形參的類型保持一致。(2)用結(jié)構(gòu)體變量作實(shí)參。老版本的C系統(tǒng)不允許用結(jié)構(gòu)體變量作實(shí)參,ANSIC取消了這一限制。但是用結(jié)構(gòu)體變量作實(shí)參時(shí),采取的是“值傳遞”的方式,將結(jié)構(gòu)體變量所占的內(nèi)存單元的內(nèi)容全部順序傳遞給形參。形參也必須是同類型的結(jié)構(gòu)體變量。在函數(shù)調(diào)用期間形參也要占用內(nèi)存單元。這種傳遞方式在空間和時(shí)間上開銷較大,如果結(jié)構(gòu)體的規(guī)模很大時(shí),開銷是很可觀的。此外,由于采用值傳遞方式,如果在執(zhí)行被調(diào)用函數(shù)期間謀淞誦尾?也是結(jié)構(gòu)體變量)的值,該值不能返回主調(diào)函數(shù),這往往造成使用上的不便。因此一般較少用這種方法。(3)用指向結(jié)構(gòu)體變量(或數(shù)組)的指針作實(shí)參,將結(jié)構(gòu)體變量(或數(shù)組)的地址傳給形參?!纠?.4】有一個(gè)結(jié)構(gòu)體變量stu,內(nèi)含學(xué)生學(xué)號(hào)、姓名和3門課的成績(jī)。要求在main函數(shù)中賦以值,在另一函數(shù)print中將它們打印輸出。今用結(jié)構(gòu)體變量作函數(shù)參數(shù)?!境绦虼a】#include<string.h>#defineFORMAT"%d\n%s\n%f\n%f\n%f\n"structstudent{intnum;charname[20];floatscore[3];};8.4.3用結(jié)構(gòu)體變量和指向結(jié)構(gòu)體的指針作函數(shù)參數(shù)
【例8.4】main(){ voidprint(structstudent);structstudentstu;stu.num=12345; strcpy(,"LiLi");stu.score[0]=67.5;stu.score[1]=89;stu.score[2]=78.6;print(stu);}voidprint(structstudentstu){ printf(FORMAT,stu.num,,stu.score[0],stu.score[1],stu.score[2]);printf("\n");}8.4.3用結(jié)構(gòu)體變量和指向結(jié)構(gòu)體的指針作函數(shù)參數(shù)
【運(yùn)行結(jié)果】8.4.3用結(jié)構(gòu)體變量和指向結(jié)構(gòu)體的指針作函數(shù)參數(shù)【例8.5】
將上題改用指向結(jié)構(gòu)體變量的指針作實(shí)參。可以在上面程序的基礎(chǔ)上作少量修改即可,請(qǐng)注意程序注釋。【程序代碼】#include<string.h>#defineFORMAT"%d\n%s\n%f\n%f\n%f\n"structstudent{ intnum;charname[10];floatscore[3];}stu={12345,"LiLi",67.5,89,78.6};8.4.3用結(jié)構(gòu)體變量和指向結(jié)構(gòu)體的指針作函數(shù)參數(shù)
main(){ voidprint(structstudent*);/*形參類型修改成指向結(jié)構(gòu)體的指針變量*/print(&stu);/*實(shí)參改為stu的起始地址*/}voidprint(structstudent*p)/*形參類型修改了*/{ printf(FORMAT,p->num,p->name,p->score[0],p->score[1],p->score[2]);/* 用 指針變量調(diào)用各成員之值*/printf("\n");}8.4.3用結(jié)構(gòu)體變量和指向結(jié)構(gòu)體的指針作函數(shù)參數(shù)【運(yùn)行結(jié)果】8.4.3用結(jié)構(gòu)體變量和指向結(jié)構(gòu)體的指針作函數(shù)參數(shù)
在C語(yǔ)言的函數(shù)調(diào)用中指針起著至關(guān)重要的作用。最重要的是,指針支持將參數(shù)作為引用傳遞給函數(shù)(即按引用調(diào)用)。按引用傳遞參數(shù)時(shí),當(dāng)函數(shù)改變此參數(shù)時(shí),這個(gè)被改變參數(shù)的值會(huì)一直存在,甚至函數(shù)退出后都仍然存在。相對(duì)而言,當(dāng)按值調(diào)用傳遞函數(shù)時(shí),此時(shí)值的改變只能持續(xù)到函數(shù)返回時(shí)。無(wú)論是否要改變函數(shù)的輸入輸出參數(shù),使用指針傳遞大容量復(fù)雜的函數(shù)參數(shù)也是十分高效的手段。這種方法高效的原因就在于,我們只是傳遞一個(gè)指針而不是一個(gè)數(shù)據(jù)的完整副本到函數(shù)中,這樣可以大大地節(jié)省內(nèi)存空間。8.4.3用結(jié)構(gòu)體變量和指向結(jié)構(gòu)體的指針作函數(shù)參數(shù)8.5用指針處理鏈表
8.5.1鏈表概述未學(xué)習(xí)鏈表的時(shí)候,如果要存儲(chǔ)數(shù)量比較多的同類型或同結(jié)構(gòu)的數(shù)據(jù)的時(shí)候,總是使用一個(gè)數(shù)組。比如說(shuō)我們要存儲(chǔ)一個(gè)班級(jí)學(xué)生的某科分?jǐn)?shù),總是定義一個(gè)float型(存在0.5分)數(shù)組:float
score[30]。但是,在使用數(shù)組的時(shí)候,總有一個(gè)問(wèn)題困擾著我們:數(shù)組應(yīng)該有多大?在很多的情況下,你并不能確定要使用多大的數(shù)組,比如上例,你可能并不知道該班級(jí)的學(xué)生的人數(shù),那么你就要把數(shù)組定義得足夠大。這樣,你的程序在運(yùn)行時(shí)就申請(qǐng)了固定大小的你認(rèn)為足夠大的內(nèi)存空間。即使你知道該班級(jí)的學(xué)生數(shù),但是如果因?yàn)槟撤N特殊原因人數(shù)有增加或者減少,你又必須重新去修改程序,擴(kuò)大數(shù)組的存儲(chǔ)范圍。這種分配固定大小的內(nèi)存分配方法稱之為靜態(tài)內(nèi)存分配。但是這種內(nèi)存分配的方法存在比較嚴(yán)重的缺陷,特別是處理某些問(wèn)題時(shí):在大多數(shù)情況下會(huì)浪費(fèi)大量的內(nèi)存空間,在少數(shù)情況下,當(dāng)你定義的數(shù)組不夠大時(shí),可能引起下標(biāo)越界錯(cuò)誤,甚至導(dǎo)致嚴(yán)重后果。鏈表則沒(méi)有這種缺點(diǎn),鏈表屬于動(dòng)態(tài)數(shù)據(jù)結(jié)構(gòu),可以類比成一環(huán)接一環(huán)的鏈條,這里每一環(huán)視作一個(gè)結(jié)點(diǎn),結(jié)點(diǎn)竄在一起形成鏈表。這種數(shù)據(jù)結(jié)構(gòu)非常靈活,結(jié)點(diǎn)數(shù)目無(wú)須事先確定,可以臨時(shí)生成。圖8.6表示最簡(jiǎn)單的一種鏈表(單向鏈表)的結(jié)構(gòu)。8.5.1鏈表概述鏈表有一個(gè)“頭指針”變量,圖中以head表示,它存放一個(gè)地址。該地址指向一個(gè)元素。鏈表中每一個(gè)元素稱為“結(jié)點(diǎn)”,每個(gè)結(jié)點(diǎn)都應(yīng)包括兩個(gè)部分:一為用戶需要用的實(shí)際數(shù)據(jù),二為下一個(gè)結(jié)點(diǎn)的地址??梢钥闯?,head指向第一個(gè)元素;第一個(gè)元素又指向第二個(gè)元素……直到最后一個(gè)元素,該元素不再指向其他元素,它稱為“表尾”,它的地址部分放一個(gè)“NULL”(表示“空地址”),鏈表到此結(jié)束??梢钥吹芥湵碇懈髟卦趦?nèi)存中可以不是連續(xù)存放的。要找某一元素,必須先找到上一個(gè)元素,根據(jù)它提供的下一元素地址才能找到下一個(gè)元素。如果不提供“頭指針”(head),則整個(gè)鏈表都無(wú)法訪問(wèn)。鏈表如同一條鐵鏈一樣,一環(huán)扣一環(huán),中間是不能斷開的??梢钥吹?,這種鏈表的數(shù)據(jù)結(jié)構(gòu),必須利用指針變量才能實(shí)現(xiàn)。即:一個(gè)結(jié)點(diǎn)中應(yīng)包含一個(gè)指針變量,用它存放下一結(jié)點(diǎn)的地址。8.5.1鏈表概述前面介紹了結(jié)構(gòu)體變量,用它作鏈表中的結(jié)點(diǎn)是最合適的。一個(gè)結(jié)構(gòu)體變量包含若干成員,這些成員可以是數(shù)值類型、字符類型、數(shù)組類型,也可以是指針類型。我們用這個(gè)指針類型成員來(lái)存放下一個(gè)結(jié)點(diǎn)的地址。例如,可以設(shè)計(jì)這樣一個(gè)結(jié)構(gòu)體類型:struct
student{ int
num;
float
score;
struct
student*next;};8.5.1鏈表概述其中成員num和score用來(lái)存放結(jié)點(diǎn)中的有用數(shù)據(jù)(用戶需要用到的數(shù)據(jù)),相當(dāng)于圖11.10結(jié)點(diǎn)中的A,B,C,D。next是指針類型的成員,它指向structstudent類型數(shù)據(jù)(這就是next所在的結(jié)構(gòu)體類型)。一個(gè)指針類型的成員既可以指向其他類型的結(jié)構(gòu)體數(shù)據(jù),也可以指向自己所在的結(jié)構(gòu)體類型的數(shù)據(jù)?,F(xiàn)在,next是structstudent類型中的一個(gè)成員,它又指向structstudent類型的數(shù)據(jù)。用這種方法就可以建立鏈表。見圖8.7。8.5.1鏈表概述圖中每一個(gè)結(jié)點(diǎn)都屬于structstudent類型,它的成員next存放下一結(jié)點(diǎn)的地址,程序設(shè)計(jì)人員可以不必具體知道各結(jié)點(diǎn)的地址,只要保證將下一個(gè)結(jié)點(diǎn)的地址放到前一結(jié)點(diǎn)的成員next中即可。請(qǐng)注意:上面只是定義了一個(gè)structstudent類型,并未實(shí)際分配存儲(chǔ)空間。只有定義了變量才分配內(nèi)存單元。8.5.1鏈表概述8.5.2簡(jiǎn)單鏈表
下面通過(guò)一個(gè)例子來(lái)說(shuō)明如何建立和輸出一個(gè)簡(jiǎn)單鏈表?!纠?.6】建立一個(gè)如圖8.7所示的簡(jiǎn)單鏈表,它由3個(gè)學(xué)生數(shù)據(jù)的結(jié)點(diǎn)組成。輸出各結(jié)點(diǎn)中的數(shù)據(jù)?!境绦虼a】#defineNULL0structstudent{longnum;floatscore;structstudent*next;};main(){ structstudenta,b,c,*head,*p;
a.num=14101;a.score=87; b.num=14103;b.score=92; c.num=14104;c.score=85.5;/*對(duì)結(jié)點(diǎn)的num和salary成員賦值*/head=&a;/*將結(jié)點(diǎn)a的起始地址賦給頭指針head*/
a.next=&b;/*將結(jié)點(diǎn)b的起始地址賦給a結(jié)點(diǎn)的next成員*/b.next=&c;/*將結(jié)點(diǎn)c的起始地址賦給b結(jié)點(diǎn)的next成員*/ c.next=NULL;/*c結(jié)點(diǎn)的next成員不存放其他結(jié)點(diǎn)地址*/p=head;/*使p指針指向a結(jié)點(diǎn)*/do{ printf("%ld%5.1f\n",p->num,p->score);/*輸出p指向的結(jié)點(diǎn)的數(shù)*/p=p->next;/*使p指向下一結(jié)點(diǎn)*/}while(p!=NULL);/*輸出完c結(jié)點(diǎn)后p的值為NULL*/}8.5.2簡(jiǎn)單鏈表【運(yùn)行結(jié)果】
請(qǐng)讀者仔細(xì)考慮:①各個(gè)結(jié)點(diǎn)是怎樣構(gòu)成鏈表的?②沒(méi)有頭指針head行不行?③p起什么作用?沒(méi)有它行不行?8.5.2簡(jiǎn)單鏈表
8.5.3處理動(dòng)態(tài)鏈表所需的函數(shù)鏈表結(jié)構(gòu)是動(dòng)態(tài)地分配存儲(chǔ)的,即在需要時(shí)才開辟一個(gè)結(jié)點(diǎn)的存儲(chǔ)單元。怎樣動(dòng)態(tài)地開辟和釋放存儲(chǔ)單元呢?C語(yǔ)言編譯系統(tǒng)的庫(kù)函數(shù)提供了以下有關(guān)函數(shù)。1、malloc函數(shù)malloc函數(shù)的原型為:void
*malloc
(unsigned
int
size)其作用是在內(nèi)存的動(dòng)態(tài)存儲(chǔ)區(qū)中分配一個(gè)長(zhǎng)度為size的連續(xù)空間。其參數(shù)是一個(gè)無(wú)符號(hào)整形數(shù),返回值是一個(gè)指向所分配的連續(xù)存儲(chǔ)域的起始地址的指針。還有一點(diǎn)必須注意的是,當(dāng)函數(shù)未能成功分配存儲(chǔ)空間(如內(nèi)存不足)就會(huì)返回一個(gè)NULL指針。所以在調(diào)用該函數(shù)時(shí)應(yīng)該檢測(cè)返回值是否為NULL并執(zhí)行相應(yīng)的操作?!纠?.7】一個(gè)動(dòng)態(tài)分配的程序【程序代碼】#include<string.h>#include<malloc.h>main()
{
int
count,*score;
/*count是一個(gè)計(jì)數(shù)器,score是一個(gè)整型指針,也可以理解為指 向一個(gè)整型數(shù)組的首地址*/
if((score=(int
*)
malloc(10*sizeof(int)))==NULL)
{
printf("不能成功分配存儲(chǔ)空間。");
exit(1);
}
for
(count=0;count<10;count++)
/*給數(shù)組賦值*/
score[count]=count;
for(count=0;count<10;count++)
/*打印數(shù)組元素*/
printf("%d",score[count]);
}
8.5.3處理動(dòng)態(tài)鏈表所需的函數(shù)【運(yùn)行結(jié)果】
上例中動(dòng)態(tài)分配了10個(gè)整型存儲(chǔ)區(qū)域,然后進(jìn)行賦值并打印。例中if((array(int
*)
malloc(10*sizeof(int)))==NULL)語(yǔ)句可以分為以下幾步:1)分配10個(gè)整型的連續(xù)存儲(chǔ)空間,并返回一個(gè)指向其起始地址的整型指針2)把此整型指針地址賦給score3)檢測(cè)返回值是否為NULL8.5.3處理動(dòng)態(tài)鏈表所需的函數(shù)2.calloc函數(shù)其函數(shù)原型為void*calloc(unsignedn,unsignedsize);其作用是在內(nèi)存的動(dòng)態(tài)區(qū)存儲(chǔ)中分配n個(gè)長(zhǎng)度為size的連續(xù)空間。函數(shù)返回一個(gè)指向分配域起始地址的指針;如果分配不成功,返回NULL。用calloc函數(shù)可以為一維數(shù)組開辟動(dòng)態(tài)存儲(chǔ)空間,n為數(shù)組元素個(gè)數(shù),每個(gè)元素長(zhǎng)度為size。8.5.3處理動(dòng)態(tài)鏈表所需的函數(shù)3、free函數(shù)由于內(nèi)存區(qū)域總是有限的,不能不限制地分配下去,而且一個(gè)程序要盡量節(jié)省資源,所以當(dāng)所分配的內(nèi)存區(qū)域不用時(shí),就要釋放它,以便其它的變量或者程序使用。這時(shí)我們就要用到free函數(shù)。其函數(shù)原型是:void
free(void
*p)作用是釋放指針p所指向的內(nèi)存區(qū),free函數(shù)無(wú)返回值。其參數(shù)p必須是先前調(diào)用malloc函數(shù)或calloc函數(shù)時(shí)返回的指針。給free函數(shù)傳遞其它的值很可能造成死機(jī)或其它災(zāi)難性的后果。8.5.3處理動(dòng)態(tài)鏈表所需的函數(shù)注意:這里重要的是指針的值,而不是用來(lái)申請(qǐng)動(dòng)態(tài)內(nèi)存的指針本身。例:int
*p1,*p2;p1=malloc(10*sizeof(int));p2=p1;……free(p1)
/*或者free(p2)*/malloc返回值賦給p1,又把p1的值賦給p2,所以此時(shí)p1,p2都可作為free函數(shù)的參數(shù)。malloc或calloc函數(shù)是對(duì)存儲(chǔ)區(qū)域進(jìn)行分配的,free函數(shù)是釋放已經(jīng)不用的內(nèi)存區(qū)域的。所以由這兩個(gè)函數(shù)就可以實(shí)現(xiàn)對(duì)內(nèi)存區(qū)域進(jìn)行動(dòng)態(tài)分配并進(jìn)行簡(jiǎn)單的管理了。8.5.3處理動(dòng)態(tài)鏈表所需的函數(shù)8.5.4建立動(dòng)態(tài)鏈表所謂建立動(dòng)態(tài)鏈表是指在程序執(zhí)行過(guò)程中從無(wú)到有地建立起一個(gè)鏈表,即一個(gè)一個(gè)地開辟結(jié)點(diǎn)和輸入各結(jié)點(diǎn)數(shù)據(jù),并建立起前后相鏈的關(guān)系?!纠?.8】寫一函數(shù)建立一個(gè)有3名學(xué)生數(shù)據(jù)的單向動(dòng)態(tài)鏈表?!窘忸}思路】學(xué)習(xí)鏈表的建立過(guò)程,關(guān)鍵是掌握3個(gè)指針的作用。頭指針head永遠(yuǎn)指向鏈表的第一個(gè)結(jié)點(diǎn);對(duì)p2指針,永遠(yuǎn)讓它指向鏈表的最末一個(gè)結(jié)點(diǎn);指針p1每次都指向一個(gè)待插入的結(jié)點(diǎn),且這個(gè)結(jié)點(diǎn)要插到q結(jié)點(diǎn)的后面。
【基本步驟】第一步:定義頭結(jié)點(diǎn)head、尾結(jié)點(diǎn)p2和待插入結(jié)點(diǎn)p1,待插入的結(jié)點(diǎn)數(shù)據(jù)部分初始化,如圖8.8所示。此步驟中,開辟結(jié)點(diǎn)和輸入數(shù)據(jù),待插入的結(jié)點(diǎn)p1數(shù)據(jù)部分初始化,并建立前后相鏈的關(guān)系,該結(jié)點(diǎn)被頭指針head、尾指針p2同時(shí)指向。第二步:重復(fù)申請(qǐng)待插入結(jié)點(diǎn)空間,對(duì)該結(jié)點(diǎn)的數(shù)據(jù)部分賦值(或輸入值),將該結(jié)點(diǎn)插入在最前面,或者最后面(本例在尾部插入),如圖8.9所示。8.5.4建立動(dòng)態(tài)鏈表8.5.4建立動(dòng)態(tài)鏈表8.5.4建立動(dòng)態(tài)鏈表8.5.4建立動(dòng)態(tài)鏈表【程序代碼】#defineNULL0#defineLENsizeof(structstudent)/*結(jié)構(gòu)體類型數(shù)據(jù)的長(zhǎng)度,sizeof是"字節(jié)數(shù)運(yùn)算符"*/structstudent{ longnum;floatscore; structstudent*next;};intn;/*n為全局變量*/structstudent*creat(void)/*定義函數(shù)。此函數(shù)返回一個(gè)指向鏈表頭的指針*/{ structstudent*head; structstudent*p1,*p2;/*p1,p2是指向結(jié)構(gòu)體類型數(shù)據(jù)的指針變量*/n=0;8.5.4建立動(dòng)態(tài)鏈表if((p1=p2=(structstudent*)malloc(LEN))==NULL)/*開辟一個(gè)長(zhǎng)度為L(zhǎng)EN內(nèi)存區(qū)并檢測(cè)*/ { printf("不能分配內(nèi)存空間!"); exit(0); } scanf("%ld,%f",&p1->num,&p1->score); head=NULL;/*假設(shè)頭指向空結(jié)點(diǎn)*/
while(p1->num!=0) { n=n+1;if(n==1)head=p1;/*頭指針指向p1結(jié)點(diǎn)*/elsep2->next=p1;/*p1開辟的新結(jié)點(diǎn)鏈到了p2的后面*/ p2=p1;8.5.4建立動(dòng)態(tài)鏈表
if((p1=(structstudent*)malloc(LEN))==NULL)/*p1繼續(xù)開辟新結(jié)點(diǎn)并檢測(cè)*/ { printf("不能分配內(nèi)存空間!"); exit(0); }scanf("%ld,%f",&p1->num,&p1->score);/*給新結(jié)點(diǎn)賦值*/}p2->next=NULL;return(head);/*返回鏈表的頭指針*/}可以在main函數(shù)中調(diào)用此creat函數(shù):main(){ …creat();/*調(diào)用creat函數(shù)后建立了一個(gè)單向動(dòng)態(tài)鏈表*/}調(diào)用creat函數(shù)后,函數(shù)的值是所建立的鏈表的第一個(gè)結(jié)點(diǎn)的地址。8.5.4建立動(dòng)態(tài)鏈表8.5.5輸出鏈表將鏈表中各結(jié)點(diǎn)的數(shù)據(jù)依次輸出。這個(gè)問(wèn)題比較容易處理。【解題思路】1.單向鏈表總是從頭結(jié)點(diǎn)開始的;2.每訪問(wèn)一個(gè)結(jié)點(diǎn),就將當(dāng)前指針向該結(jié)點(diǎn)的下一個(gè)結(jié)點(diǎn)移動(dòng):
p=p->next;3.直至下一結(jié)點(diǎn)為空
P=NULL以上步驟可用圖8.16表示【例8.9】編寫一個(gè)輸出鏈表的函數(shù)print?!境绦虼a】voidprint(structstudent*head){ structstudent*p;printf("\nNow,These%drecordsare:\n",n);p=head;if(head!=NULL)
do{ printf("%ld%5.lf\n",p->num,p->score);
p=p->next;}while(p!=NULL);}8.5.4建立動(dòng)態(tài)鏈表8.5.6對(duì)鏈表的刪除操作從一個(gè)動(dòng)態(tài)鏈表中刪去一個(gè)結(jié)點(diǎn),并不是真正從內(nèi)存中把它抹掉,而是把它從鏈表中分離開來(lái),只要撤消原來(lái)的鏈接關(guān)系即可。【例8.10】寫一函數(shù)以刪除動(dòng)態(tài)鏈表中指定的結(jié)點(diǎn)。【解題思路】需要設(shè)定兩個(gè)臨時(shí)指針:p1:判斷指向的結(jié)點(diǎn)是不是要?jiǎng)h除的結(jié)點(diǎn)(用于尋找);p2:始終指向P1的前面一個(gè)結(jié)點(diǎn);從p1指向的第一個(gè)結(jié)點(diǎn)開始,檢查該結(jié)點(diǎn)中的num值是否等于輸入的要求刪除的那個(gè)學(xué)號(hào)。如果相等就將該結(jié)點(diǎn)刪除,如不相等,就將p1后移一個(gè)結(jié)點(diǎn),在此之前應(yīng)將p1的值賦給p2,使p2指向剛才檢查過(guò)的那個(gè)結(jié)點(diǎn)。再如此進(jìn)行下去,直到遇到表尾為止。8.5.6對(duì)鏈表的刪除操作刪除的節(jié)點(diǎn)需要考慮兩種情況:1、要?jiǎng)h的結(jié)點(diǎn)是頭指針?biāo)傅慕Y(jié)點(diǎn)則直接操作。原鏈表p1指向頭結(jié)點(diǎn),如圖8.19所示。8.5.6對(duì)鏈表的刪除操作2、不是頭結(jié)點(diǎn),要依次往下找。p2指向p1指向的結(jié)點(diǎn)。p1指向下移一個(gè)結(jié)點(diǎn),如圖8.21所示。8.5.6對(duì)鏈表的刪除操作經(jīng)P1找到要?jiǎng)h除的結(jié)點(diǎn)后使之脫離,p1->next賦給p2->next,如圖8.22所示。另外還要考慮的情況:空表和找不到要?jiǎng)h除的結(jié)點(diǎn)。8.5.6對(duì)鏈表的刪除操作【程序代碼】structstudent*del(structstudent*head,longnum){ structstudent*p1,*p2; if(head==NULL) { printf("\nlistnull!\n"); gotoend; } p1=head;8.5.6對(duì)鏈表的刪除操作 while(num!=p1->num&&p1->next!=NULL)/*p1指向的不是所要找的結(jié)點(diǎn),并且后面還有結(jié)點(diǎn)*/ { p2=p1; p1=p1->next; }/*p1后移一個(gè)結(jié)點(diǎn)*/if(num==p1->num)/*找到了*/ { if(p1==head) head=p1->next;/*若p1指向的是首結(jié)點(diǎn),把第二個(gè)結(jié)點(diǎn)地址賦予head*/ else p2->next=p1->next;/*否則將下一結(jié)點(diǎn)地址賦給前一結(jié)點(diǎn)地址*/
printf("delete:%ld\n",num); n=n-1; }8.5.6對(duì)鏈表的刪除操作else printf("%ldnotbeenfound!\n",num);/*找不到該結(jié)點(diǎn)*/end: return(head);}函數(shù)的類型是指向structstudent類型數(shù)據(jù)的指針,它的值是鏈表的頭指針。函數(shù)參數(shù)為head和要?jiǎng)h除的學(xué)號(hào)num。head的值可能在函數(shù)執(zhí)行過(guò)程中被改變(當(dāng)刪除第一個(gè)結(jié)點(diǎn)時(shí))。8.5.6對(duì)鏈表的刪除操作8.5.7對(duì)鏈表的插入操作對(duì)鏈表的插入是指將一個(gè)結(jié)點(diǎn)插入到一個(gè)已有的鏈表中?!纠?.11】寫一函數(shù)以刪除動(dòng)態(tài)鏈表中指定的結(jié)點(diǎn)?!窘忸}思路]節(jié)點(diǎn)插入的位置共有四種情況:1、第一種情況,鏈表還未建成(空鏈表),待插入結(jié)點(diǎn)p0實(shí)際上是第一個(gè)結(jié)點(diǎn),如圖8.23所示。2、第二種情況,鏈表已建成,待插入結(jié)點(diǎn)p0的數(shù)據(jù)要比頭結(jié)點(diǎn)的數(shù)據(jù)還要小,這時(shí)當(dāng)然p0結(jié)點(diǎn)要插在head結(jié)點(diǎn)前,如圖8.24示。8.5.6對(duì)鏈表的刪除操作3、第三種情況,鏈表已建成,待插入結(jié)點(diǎn)p0的數(shù)據(jù)比頭結(jié)點(diǎn)的數(shù)據(jù)大,需要找到正確的插入位置。這時(shí),可以借助兩個(gè)結(jié)構(gòu)指針p1和p2,利用循環(huán)比較來(lái)找到正確位置。然后將結(jié)點(diǎn)p0插入到鏈表中正確的位置,如圖8.25-8.26所示。8.5.6對(duì)鏈表的刪除操作8.5.6對(duì)鏈表的刪除操作4、第四種情況,鏈表已建成,待插入結(jié)點(diǎn)p0的數(shù)據(jù)要比所有結(jié)點(diǎn)的數(shù)據(jù)都要大,這時(shí)當(dāng)然p0結(jié)點(diǎn)要插在表尾,如圖8.27所示。8.5.6對(duì)鏈表的刪除操作【程序代碼】
structstudent*insert(structstudent*head,structstudent*stud){ structstudent*p0,*p1,*p2; p1=head; /*使p1指向第一個(gè)結(jié)點(diǎn)*/ p0=stud;/*p0指向要插入的結(jié)點(diǎn)*/ if(head==NULL)/*原來(lái)的鏈表是空表*/ { head=p0; p0->next=NULL; }/*使p0指向的結(jié)點(diǎn)作為頭結(jié)點(diǎn)*/ else { while((p0->num>p1->num)&&(p1->next!=NULL)) { p2=p1;/*使p2指向剛才p1指向的結(jié)點(diǎn)*/ p1=p1->next; /*p1后移一個(gè)結(jié)點(diǎn)*/ }
8.5.6對(duì)鏈表的刪除操作if(p0->num<p1->num) { if(head==p1)head=p0;/*插到原來(lái)第一個(gè)結(jié)點(diǎn)之前*/ elsep2->next=p0;/*插到p2指向的結(jié)點(diǎn)之后*/ p0->next=p1; } else { p1->next=p0; /*插到最后的結(jié)點(diǎn)之后*/ p0->next=NULL; } }n=n+1;/*結(jié)點(diǎn)數(shù)加1*/return(head);}函數(shù)參數(shù)是head和stud。stud也是一個(gè)指針變量,從實(shí)參傳來(lái)待插入結(jié)點(diǎn)的地址給stud。語(yǔ)句p0=stud的作用是使p0指向待插入的結(jié)點(diǎn)。函數(shù)類型是指針類型,函數(shù)值是鏈表起始地址head。8.5.6對(duì)鏈表的刪除操作8.5.8對(duì)鏈表的綜合操作將以上建立、輸出、刪除、插入的函數(shù)組織在一個(gè)C程序中,即將例8.8~8.11中的4個(gè)函數(shù)順序排列,用main函數(shù)作主調(diào)函數(shù)??梢詫懗鲆韵耺ain函數(shù)(main函數(shù)的位置在以上各函數(shù)的后面)。#include<stdio.h>#include<malloc.h> main() { structstudent*head,*stu; longdel_num; printf("inputrecords:\n"); head=creat();/*返回頭指針*/ print(head);/*輸出全部結(jié)點(diǎn)*/ printf("\ninputthedeletednumber:");scanf("%ld",&del_num); /*輸入要?jiǎng)h除的學(xué)號(hào)*/ head=del(head,del_num);/*刪除后鏈表的頭地址*/ print(head); /*輸出全部結(jié)點(diǎn)*/ printf("\ninputtheinsertedrecord:"); /*輸入要插入的結(jié)點(diǎn)*/ if((stu=(structstudent*)malloc(LEN))==NULL)/*開辟一個(gè)長(zhǎng)度為L(zhǎng)EN內(nèi)存區(qū)并檢測(cè)*/ { printf("不能分配內(nèi)存空間!"); exit(0); } scanf("%ld,%f",&stu->num,&stu->score); head=insert(head,stu);/*返回地址*/ print(head);/*輸出全部結(jié)點(diǎn)*/ }8.5.8對(duì)鏈表的綜合操作【運(yùn)行結(jié)果】
8.5.8對(duì)鏈表的綜合操作8.6共用體
在C語(yǔ)言中,允許不同數(shù)據(jù)類型使用同一存儲(chǔ)區(qū)域,共用體就是一種同一存儲(chǔ)區(qū)域由不同類型變量共享的數(shù)據(jù)類型。它提供—種方法能在同一存儲(chǔ)區(qū)中操作不同類型的數(shù)據(jù),也就是說(shuō)共用體采用的是覆蓋存儲(chǔ)技術(shù),準(zhǔn)許不同類型數(shù)據(jù)互相覆蓋。例如,可把一個(gè)整型變量、一個(gè)字符型變量、一個(gè)實(shí)型變量放在同一個(gè)地址開始的內(nèi)存單元中(見圖8.28)。以上3個(gè)變量在內(nèi)存中占的字節(jié)數(shù)不同,但都從同一地址開始(圖中設(shè)地址為1000)存放。8.6.1共用體類型定義共用體類型的定義與結(jié)構(gòu)體類似,其一般定義格式如下:union共用體名{
共用體成員表;};其中union是關(guān)鍵字,稱為共用體定義標(biāo)識(shí)符,共用體名同樣由程序員來(lái)命名。大括號(hào)中的共用體成員表包含若干成員,每一個(gè)成員都具有如下的形式:
數(shù)據(jù)類型標(biāo)識(shí)符成員名;如:uniondata{ inti; charch; floatf;};8.6.2共用體變量定義與引用1.共用體變量的定義union共用體名{
共用體成員表;}變量列表;如:uniondata{ inti; charch; floatf;}a,b,c;共用體的定義格式與結(jié)構(gòu)類型的定義和變量聲明形式上類似,但實(shí)質(zhì)上有區(qū)別:(1)結(jié)構(gòu)類型的長(zhǎng)度=各成員的長(zhǎng)度和;各成員占獨(dú)立的存儲(chǔ)單元,不共享;(2)聯(lián)合類型的長(zhǎng)度為成員中長(zhǎng)度的最大者,各成員共享長(zhǎng)度最大的存儲(chǔ)單元;2.共用體變量的引用引用格式:共用體變量名.成員名;如上例所示:a.i;a.ch;a.f說(shuō)明:(1)共用體變量不能同時(shí)存放多個(gè)成員的值,而只能存放其中一個(gè)值,即只能存放當(dāng)前(最新)的一個(gè)成員的值;(2)就共用體變量整體而言,和結(jié)構(gòu)體變量一樣是不能進(jìn)行整體的輸入、輸出,但可以在兩個(gè)同一類型的共用體變量之間賦值;(3)由于共用體變量不能同時(shí)存放多個(gè)成員的值,因此共用體變量不能進(jìn)行初始化;(4)共用變量不可作為函數(shù)的參數(shù),但可以通過(guò)指針指向;(5)共用體類型可以和結(jié)構(gòu)類型/數(shù)組類型互為基類型。8.6.2共用體變量定義與引用【例8.12】設(shè)有若干個(gè)人員的數(shù)據(jù),其中有學(xué)生和教師。學(xué)生的數(shù)據(jù)中包括:姓名、號(hào)碼、性別、職業(yè)、班級(jí)。教師的數(shù)據(jù)包括:姓名、號(hào)碼、性別、職業(yè)、職務(wù)。可以看出,學(xué)生和教師所包含的數(shù)據(jù)是不同的?,F(xiàn)要求把它們放在同一表格中,見圖8.29。如果“job”項(xiàng)為“s”(學(xué)生),則第5項(xiàng)為class(班)。即Li是501班的。如果“job”項(xiàng)是“t”(教師),則第5項(xiàng)為position(職務(wù))。Wang是prof(教授)。顯然對(duì)第5項(xiàng)可以用共用體來(lái)處理(將class和position放在同一段內(nèi)存中)。8.6.2共用體變量定義與引用8.6.2共用體變量定義與引用【程序代碼】#include<stdio.h>struct{ intnum; charname[10]; charsex; charjob; union { intclass; charposition[10]; }category;}person[2];8.6.2共用體變量定義與引用main(){ inti; for(i=0;i<2;i++) {scanf("%d%s%c%c",&person[i].num,person[i].name,&person[i].sex,&pers on[i].job); if(person[i].job=='s') scanf("%d",&person[i].category.class); elseif(person[i].job=='t') scanf("%s",person[i].category.position); else printf("inputerror!"); }8.6.2共用體變量定義與引用printf("\n"); printf("No.Namesexjobclass/position\n"); for(i=0;i<2;i++) { if(person[i].job=='s') printf("%6d%10s%3c%3c%6d\n",person[i].num, person[i].name,person[i].sex,person[i].job, person[i].category.class); else printf("%6d%10s%3c%3c%6s\n",person[i].num, person[i].name,person[i].sex,person[i].job, person[i].category.position); }}8.6.2共用體變量定義與引用【運(yùn)行結(jié)果】可以看到:在main函數(shù)之前定義了外部的結(jié)構(gòu)體數(shù)組person,在結(jié)構(gòu)體類型聲明中包括了共用體類型,category(分類)是結(jié)構(gòu)體中一個(gè)成員名,在這個(gè)共用體中成員為calss和position,前者為整型,后者為字符數(shù)組(存放“職務(wù)”的值——字符串)。8.6.2共用體變量定義與引用8.7枚舉類型
在實(shí)際應(yīng)用中,有些變量的取值范圍是有限的,僅可能只有幾個(gè)值,如一個(gè)星期7天,一年12個(gè)月,一副撲克有4種花色,每一花色有13張牌等等。此時(shí)用整型數(shù)來(lái)表示這些變量的取值,其直觀性很差,如在程序中使用1,對(duì)于非編稱者來(lái)說(shuō),它是代表星期一呢?還是一月份?很難區(qū)分。若在程序中使用“Mon”,則不會(huì)有人認(rèn)為是代表一月份。由此看出,為提高程序的可讀性,引入非數(shù)值量,即一些有意義的符號(hào)是非常必要的。對(duì)于這種應(yīng)用,C語(yǔ)言引入枚舉類型,所謂“枚舉”,就是將變量可取的值一一列舉出來(lái)。聲明枚舉類型用enum開頭。例如:enumweekday{ sun,mon,tue,wed,thu,fri,sat};由此定義了一個(gè)枚舉類型enumweekday,它有7個(gè)枚舉元素(常量)。在定義了類型之后,就可以用該類型來(lái)定義變量:如:enumweekdayworkday;當(dāng)然,也可以直接定義枚舉變量,如:enum{sun,mon,tue,wed,thu,fri,sat}workday,week-end;其中sun、mon、…、sat等稱為枚舉元素或枚舉常量。它們是用
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 《電氣控制原理圖》課件
- DB32T-建筑工程BIM規(guī)劃報(bào)建數(shù)據(jù)規(guī)范編制說(shuō)明
- 給予是快樂(lè)的課件公開課專用
- 《口腔潔治課件》課件
- 基因工程的基本操作程序課件
- 《TA溝通分析課程》課件
- 《伊犁河大橋》課件
- 生活處處有哲學(xué)課件
- 單位管理制度展示匯編【員工管理篇】
- 中國(guó)武都頭風(fēng)痛丸項(xiàng)目投資可行性研究報(bào)告
- 中醫(yī)五臟課件
- 安谷鐵龍煤礦整合技改施工組織設(shè)計(jì)樣本
- 《新概念英語(yǔ)第二冊(cè)》電子書、單詞、筆記、練習(xí)冊(cè)(附答案)匯編
- 2023年云南大學(xué)滇池學(xué)院招聘考試真題
- 品質(zhì)助理述職報(bào)告
- 2023-2024學(xué)年湖南省長(zhǎng)沙市雨花區(qū)外研版(三起)五年級(jí)上冊(cè)期末質(zhì)量檢測(cè)英語(yǔ)試卷
- 超越指標(biāo):存量時(shí)代降本增效的利器
- 部隊(duì)休假安全教育課件
- 2024縣級(jí)應(yīng)急廣播系統(tǒng)技術(shù)規(guī)范
- 一年級(jí)道德與法治無(wú)紙筆期末檢測(cè)質(zhì)量分析
- 視頻剪輯師工作總結(jié)
評(píng)論
0/150
提交評(píng)論