C語言第8章 復(fù)合數(shù)據(jù)類型_第1頁
C語言第8章 復(fù)合數(shù)據(jù)類型_第2頁
C語言第8章 復(fù)合數(shù)據(jù)類型_第3頁
C語言第8章 復(fù)合數(shù)據(jù)類型_第4頁
C語言第8章 復(fù)合數(shù)據(jù)類型_第5頁
已閱讀5頁,還剩62頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

第8章復(fù)合數(shù)據(jù)類型本章主要內(nèi)容掌握結(jié)構(gòu)類型、結(jié)構(gòu)變量、結(jié)構(gòu)數(shù)組、結(jié)構(gòu)指針的定義和引用方法掌握結(jié)構(gòu)變量及結(jié)構(gòu)數(shù)組在函數(shù)間的傳遞規(guī)則,能夠用結(jié)構(gòu)進行鏈表的簡單操作了解聯(lián)合、位段及枚舉類型的概念、定義和引用,學(xué)會已有類型的別名定義方法。

8.1結(jié)構(gòu)類型在C語言中,結(jié)構(gòu)類型常用來處理像“記錄”、“鏈表”這樣的靜態(tài)和動態(tài)數(shù)據(jù)結(jié)構(gòu)。例如,要描述一本圖書記錄,包括書號、書名、作者、出版社、出版時間和定價等數(shù)據(jù)項,可以把它們組合在一起,形成一個新的數(shù)據(jù)類型,稱為結(jié)構(gòu)類型。其中書號、書名、作者、出版社和定價等稱為結(jié)構(gòu)的成員,它們可以是不同類型的數(shù)據(jù)。結(jié)構(gòu)類型是由不同數(shù)據(jù)類型變量組成的集合體,相當(dāng)于我們常說的記錄。結(jié)構(gòu)成員結(jié)構(gòu)記錄數(shù)據(jù)項

書號書名作者出版社定價8.1.1結(jié)構(gòu)類型的定義和存儲模式定義結(jié)構(gòu)類型包括定義結(jié)構(gòu)類型的名稱及其包含的成員。

struct

結(jié)構(gòu)標(biāo)識符

{

數(shù)據(jù)類型成員1;

數(shù)據(jù)類型成員2;...

數(shù)據(jù)類型成員n;};其中,struct是關(guān)鍵字,結(jié)構(gòu)標(biāo)識符和各成員由用戶自行命名;關(guān)鍵字struct連同其后的結(jié)構(gòu)標(biāo)識符一起稱為結(jié)構(gòu)類型名或結(jié)構(gòu)名;各成員的定義語句放在花括號中構(gòu)成復(fù)合語句,花括號后面的分號是整個定義語句的結(jié)尾。1.結(jié)構(gòu)類型的定義由于結(jié)構(gòu)類型包含的成員組合可能千差萬別,所以,結(jié)構(gòu)類型不是惟一的,不能像int、float那樣用一個固定的關(guān)鍵字來描述,而是讓用戶自己去定義結(jié)構(gòu)類型的名稱及其成員。上面提到的圖書記錄下圖所示。書號書名作者出版社定價charnumber[8]charbookname[20]charauthor[10]charpress[10]floatprice可以將該圖書記錄定義成一個名為structBook的結(jié)構(gòu)類型:

structBook{charnumber[8];charbookname[20];charauthor[10];charpress[10];floatprice;};①每個成員都必須有自己的數(shù)據(jù)類型,位置上連續(xù)的同類型的結(jié)構(gòu)成員可以出現(xiàn)在一條語句中,并共用同一個類型關(guān)鍵字。②結(jié)構(gòu)類型的成員可以是基本數(shù)據(jù)類型的變量、數(shù)組或指針,也可以是已定義結(jié)構(gòu)類型的變量、數(shù)組或指針。③結(jié)構(gòu)成員可以和程序中的其他標(biāo)識符同名,也可以和另一個結(jié)構(gòu)的成員同名。④結(jié)構(gòu)類型定義的位置,可以在函數(shù)內(nèi)部,也可以在函數(shù)外部。在函數(shù)內(nèi)部定義的結(jié)構(gòu)類型,只能在函數(shù)內(nèi)部使用;在函數(shù)外部定義的結(jié)構(gòu)類型,其有效范圍是從定義處開始,直到它所在的源程序文件結(jié)束。2.在定義結(jié)構(gòu)類型時需要注意的問題定義了結(jié)構(gòu)類型,并沒有實際分配內(nèi)存,只是規(guī)定了內(nèi)存分配模式。如果有用已定義的結(jié)構(gòu)類型定義了結(jié)構(gòu)變量或結(jié)構(gòu)數(shù)組以后,就會按這種模式分配內(nèi)存。結(jié)構(gòu)類型的內(nèi)存分配模式是:各成員按照先后順序,根據(jù)自身的數(shù)據(jù)類型占用連續(xù)的存儲單元。當(dāng)程序中定義了某種結(jié)構(gòu)類型的變量或數(shù)組后,編譯系統(tǒng)將按照該結(jié)構(gòu)的內(nèi)存分配模式為結(jié)構(gòu)變量及數(shù)組安排一片連續(xù)的存儲單元。3.結(jié)構(gòu)類型的內(nèi)存分配模式首地址數(shù)據(jù)項占用字節(jié)number[8]8字節(jié)bookname[20]20字節(jié)author[10]10字節(jié)press[10]10字節(jié)price4字節(jié)結(jié)構(gòu)類型所需要的內(nèi)存空間字節(jié)數(shù)是各成員所需空間字節(jié)數(shù)的總和,可以用sizeof(結(jié)構(gòu)類型名)來得到所需字節(jié)數(shù)。結(jié)構(gòu)變量、結(jié)構(gòu)數(shù)組或結(jié)構(gòu)指針指的是某種結(jié)構(gòu)類型的變量、數(shù)組或指針。它們既可以在定義結(jié)構(gòu)類型的同時定義,也可以先定義結(jié)構(gòu)類型,再用結(jié)構(gòu)類型名定義。8.1.2結(jié)構(gòu)變量、結(jié)構(gòu)數(shù)組和結(jié)構(gòu)指針的定義和初始化1.結(jié)構(gòu)變量、結(jié)構(gòu)數(shù)組和結(jié)構(gòu)指針的定義①直接定義在定義結(jié)構(gòu)類型的同時定義該類型的變量、數(shù)組及指針。有名結(jié)構(gòu)類型:關(guān)鍵字后帶結(jié)構(gòu)標(biāo)識符。

struct

Book{charnumber[8];charbookname[20];charauthor[10];charpress[10];floatprice;}book1,book2[2],*book3;

無名結(jié)構(gòu)類型:關(guān)鍵字struct后面不帶結(jié)構(gòu)標(biāo)識符。

struct

{charnumber[8];charbookname[20];charauthor[10];charpress[10];floatprice;}book1,book2[2],*book3;注意:(1)有名結(jié)構(gòu)類型的類型名可以多次用來定義該類型的變量、數(shù)組或指針。(2)無名結(jié)構(gòu)類型只能在定義的同時,一次性地定義該類型的變量、數(shù)組或指針。②間接定義先定義結(jié)構(gòu)類型,再用結(jié)構(gòu)類型名定義該類型的變量、數(shù)組及指針。

structBook{charnumber[8];charbookname[20];charauthor[10];charpress[10];floatprice;};/*定義結(jié)構(gòu)類型*/

structBookbook1,book2[2],*book3;

/*定義變量*/如果一個結(jié)構(gòu)的某些成員屬于另一種結(jié)構(gòu)類型,稱之為嵌套結(jié)構(gòu)。例如,將上述圖書記錄增加一個成員——出版日期,該成員是另一種結(jié)構(gòu)類型的變量ymd:structDate{intyear,month,day;}ymd;此時,可定義結(jié)構(gòu)類型structBook如下:

2.嵌套結(jié)構(gòu)structBook{charnumber[5];charbookname[10];charauthor[10];charpress[10];

structdate{int

year,month,day;}ymd;floatprice;};structBook{charnumber[5];charbookname[10];charauthor[10];charpress[10];

structDateymd;//嵌套結(jié)構(gòu)成員

floatprice;};

由于結(jié)構(gòu)類型是若干個成員組成的整體,要占用一片連續(xù)的存儲單元,因此,結(jié)構(gòu)變量的初始化形式與前面介紹的一維數(shù)組相似,只要把對應(yīng)各成員的初值放在花括號(即初值表)中。結(jié)構(gòu)數(shù)組的初始化則與二維數(shù)組相似,只要將各個元素的初值順序放在內(nèi)嵌的花括號中,當(dāng)整個數(shù)組的各個元素都有初始化數(shù)據(jù)時,內(nèi)嵌花括號也可以省略。例如:structBookbook1={"10035","計算機網(wǎng)絡(luò)","吳明","高等教育出版社",2008,10,25,18.5};//結(jié)構(gòu)變量初始化structBookbook2[2]={{"20001","操作系統(tǒng)教程","周靜文","北京大學(xué)出版社",2010,1,1,25},{"20002","石頭記","吳承恩","商務(wù)書局",2004,8,3,38.5}};//結(jié)構(gòu)數(shù)組初始化structBook*book3=book2;//結(jié)構(gòu)指針book3初始化指向結(jié)構(gòu)數(shù)組book23.結(jié)構(gòu)變量、結(jié)構(gòu)數(shù)組和結(jié)構(gòu)指針的初始化①結(jié)構(gòu)變量或結(jié)構(gòu)數(shù)組初始化后,C編譯系統(tǒng)將安排連續(xù)的存儲空間,按結(jié)構(gòu)成員的順序?qū)⒏鱾€初值置于各成員對應(yīng)的存儲單元。②在對結(jié)構(gòu)變量初始化時,不允許用逗號跳過前面的成員只給后面的成員賦初值,但可以只給前面的成員賦初值,后面未賦初值的成員自動賦0值(字符型數(shù)組賦空串)。③像數(shù)組的初始化一樣,如果結(jié)構(gòu)數(shù)組的全部元素均已在初值表中列出,或各元素的初值表都用花括號括住,也可以定義隱含尺寸的結(jié)構(gòu)數(shù)組,如:

structkey{charword[20];

intcount;}keytab[]={"auto",0,"break",0,"case",0,"char",0,"const",0,"continue",0,"unsigned",0,"void",0,"volatile",0,"white",0};其中,keytab是一個隱含尺寸數(shù)組,隱含的維界是10。④結(jié)構(gòu)指針與前面介紹的基本類型的指針相比,除了所指向的對象不同外,在存儲形式和使用形式上并沒有差別。因此,可以通過用結(jié)構(gòu)變量的首地址或結(jié)構(gòu)數(shù)組的數(shù)組名作它的初值,從而使它指向?qū)?yīng)的結(jié)構(gòu)變量或結(jié)構(gòu)數(shù)組。幾點說明:由于結(jié)構(gòu)變量包含若干不同類型的成員,而結(jié)構(gòu)數(shù)組既包含數(shù)組元素,每個元素又包含不同的成員,因此,訪問或引用結(jié)構(gòu)變量或結(jié)構(gòu)數(shù)組元素的某個成員,必須指明該成員屬于哪一個結(jié)構(gòu)變量或數(shù)組元素。

C語言提供了兩種訪問結(jié)構(gòu)成員的運算符:“.”和“->”,它們都是二元運算符,和“()”、“[]”一起處于最高優(yōu)先級,結(jié)合性均為從左至右。

①“.”運算符(俗稱點運算符)適合于用結(jié)構(gòu)變量或結(jié)構(gòu)數(shù)組元素訪問其成員。②“->”運算符適合于用結(jié)構(gòu)數(shù)組或結(jié)構(gòu)指針訪問其成員。8.1.3訪問結(jié)構(gòu)變量和結(jié)構(gòu)數(shù)組的成員1.訪問結(jié)構(gòu)成員運算符若已定義了一個結(jié)構(gòu)變量或結(jié)構(gòu)數(shù)組及指向它們的指針,要訪問其成員,可以使用以下三種形式:①結(jié)構(gòu)變量.成員名或結(jié)構(gòu)數(shù)組元素.成員名②結(jié)構(gòu)指針->成員名或結(jié)構(gòu)數(shù)組->成員名③(*結(jié)構(gòu)指針).成員名最后一種形式中的“(*結(jié)構(gòu)指針名)”實際上是一種等價的結(jié)構(gòu)變量名或結(jié)構(gòu)數(shù)組元素,因此,要用“.”來訪問。若結(jié)構(gòu)成員是內(nèi)嵌的結(jié)構(gòu)時,必須用若干個“.”或“->”來順序逐層定位到最里層的成員。2.訪問結(jié)構(gòu)成員的方法若定義如下結(jié)構(gòu)變量、結(jié)構(gòu)數(shù)組和結(jié)構(gòu)指針:

struct

st{charstr[5],s[10];

inta;

struct

{charc;floatw;}d;}

struct

stx,y[10],*px=&x,*py=y;

則訪問結(jié)構(gòu)成員(包括變量、數(shù)組、指針和內(nèi)嵌結(jié)構(gòu))的各種形式列于下表。結(jié)構(gòu)變量結(jié)構(gòu)數(shù)組元素結(jié)構(gòu)數(shù)組或指針成員str[i]x.str[i](*px).str[i]y[j].str[i]py[j].str[i]px->str[i](y+j)->str[i](py+j)->str[i]成員str首地址x.str(*px).stry[j].strpy[j].strpx->str(y+j)->str(py+j)->str成員str[i]地址&x.str[i]&(*px).str[i]&y[j].str[i]&py[j].str[i]&px->str[i]&(y+j)->str[i]&(py+j)->str[i]成員ax.a(*px).ay[j].apy[j].apx->a(y+j)->a(py+j)->a成員a地址&x.a&(*px).a&y[j].a&py[j].a&px->a&(py+j)->a&(y+j)->a成員cx.d.c(*px).d.cy[j].d.cpy[j].d.c(py+j)->d.c(y+j)->d.cy[j].d.c成員c地址&x.d.c&px->d.c&(*px).d.c&y[j].d.c&py[j].d.cpx->d.c&(py+j)->d.c&(y+j)->d.c

C語言允許用一個結(jié)構(gòu)變量對另一個同類型的結(jié)構(gòu)變量整體賦值,例如:

struct

stx1,x2={"101","abcd",28,'n',45.8};x1=x2;

這時,結(jié)構(gòu)變量x2的各個成員分別對同類型結(jié)構(gòu)變量x1的對應(yīng)成員賦值。除此之外,C語言不允許對結(jié)構(gòu)變量名直接進行賦值、輸入和輸出,只能對它的每個成員賦值、輸入和輸出。也就是說,要將結(jié)構(gòu)變量的成員作為相對獨立的個體對待。8.1.4結(jié)構(gòu)變量、結(jié)構(gòu)數(shù)組和結(jié)構(gòu)指針的賦值、輸入和輸出

#include<stdio.h>#include<string.h>

struct

st{charstr[5],s[10];

inta;

struct

{charc;floatw;}d;//定義嵌套結(jié)構(gòu)類型

};voidmain(){struct

stx1,x2;gets(x1.str);gets(x1.s);x1.a=34,x1.d.c='F',x1.d.w=23.5f;x2=x1;//兩個同類型結(jié)構(gòu)變量之間的賦值

printf("%4s%-5s%3d%c%.2f\n",x2.str,x2.s,x2.a,x2.d.c,x2.d.w);}

【例】結(jié)構(gòu)變量的賦值、輸入和輸出。

#include<stdio.h>#include<string.h>

struct

st{charstr[5],s[10];

inta;

struct

{charc;floatw;}d;};【例】結(jié)構(gòu)數(shù)組的賦值、輸入和輸出。voidmain(){inti;

struct

stx[3];for(i=0;i<2;i++){printf("Input2strings:\n");

scanf("%s%*c",x[i].str);

scanf("%s",x[i].s);

printf("Inputint/char/floatdata:\n");

scanf("%d%*c",&x[i].a);

scanf("%c",&x[i].d.c);scanf("%f",&x[i].d.w);

}x[2]=x[0];

for(i=0;i<3;i++)printf("%5s%-9s%3d%c%.2f\n",x[i].str,x[i].s,x[i].a,x[i].d.c,x[i].d.w);}結(jié)構(gòu)類型的數(shù)據(jù)也可以在函數(shù)間傳遞,傳遞的方式也有參數(shù)傳遞、函數(shù)返回值和全局結(jié)構(gòu)三種。全局結(jié)構(gòu)在函數(shù)外定義,可提供各個函數(shù)共享,但不提倡使用,下面僅介紹前兩種傳遞方式。8.2結(jié)構(gòu)在函數(shù)間的傳遞在參數(shù)傳遞方式下,可以通過傳遞結(jié)構(gòu)成員、傳遞結(jié)構(gòu)變量和結(jié)構(gòu)數(shù)組,還可以通過結(jié)構(gòu)指針來傳遞數(shù)據(jù)。采用參數(shù)傳遞方式時,最好定義一個全局結(jié)構(gòu)類型(即外部定義),以保證實參和形參類型一致。(1)結(jié)構(gòu)變量的整體傳遞如果調(diào)用函數(shù)要將結(jié)構(gòu)變量整體傳遞給被調(diào)用函數(shù),可以采用以下三種形式:①實參和形參都是同類型的結(jié)構(gòu)變量名②實參是結(jié)構(gòu)變量的地址,形參是同結(jié)構(gòu)類型的指針③實參和形參都是同結(jié)構(gòu)類型的指針1.參數(shù)傳遞方式

#include<stdio.h>#include<string.h>

structsample//定義全局結(jié)構(gòu)類型

{int

a,b;charch[10];};voidfun(structsampleparm)//形參為結(jié)構(gòu)變量

{parm.a*=parm.b;parm.ch[2]='x';

printf("Sub:a=%d,ch=%s\n",parm.a,parm.ch);}

main(void){structsamplearg={100,10,"ABCD"};

fun(arg);//結(jié)構(gòu)變量的傳遞

printf("Main:a=%d,ch=%s\n",arg.a,arg.ch);}【例】定義一個結(jié)構(gòu)變量,其成員包括兩個整數(shù)a和b及一個字符串ch。主程序?qū)⒃摻Y(jié)構(gòu)變量傳遞給自定義函數(shù)fun(),fun()函數(shù)計算a*b并保存在a中,同時將字符串ch中第3個字符替換為'x',最后輸出處理結(jié)果。

#include<stdio.h>#include<string.h>

structsample{int

a,b;charch[10];};voidfun(structsample*p)//形參為結(jié)構(gòu)指針

{p->a*=p->b;p->ch[2]='x';

printf("Sub:a=%d,ch=%s\n",p->a,p->ch);}

main(void){structsamplearg={100,10,"ABCD"};

fun(&arg);//結(jié)構(gòu)指針的傳遞

printf("Main:a=%d,ch=%s\n",arg.a,arg.ch);}【例】用結(jié)構(gòu)指針來傳遞結(jié)構(gòu)型數(shù)據(jù),修改前例。(2)結(jié)構(gòu)數(shù)組的傳遞如果調(diào)用程序要將結(jié)構(gòu)數(shù)組傳遞被調(diào)用函數(shù),則可以用以下兩種形式:①實參是結(jié)構(gòu)數(shù)組名,形參是同類型的結(jié)構(gòu)數(shù)組名或結(jié)構(gòu)指針。②實參和形參都是同類型的結(jié)構(gòu)指針。

【例】

建立一個通訊錄:input()用于輸入通訊錄數(shù)據(jù),display()用于輸出通訊錄,main()函數(shù)向input()和display()函數(shù)傳遞結(jié)構(gòu)數(shù)組,用于函數(shù)間的數(shù)據(jù)傳遞。

#include<stdio.h>#defineMAX3

structTXL{charname[10];charmobile[12];charphone[12];};voidinput(structTXLp[])//通信錄輸入:結(jié)構(gòu)數(shù)組作形參

{inti;for(i=0;i<MAX;i++){printf("Name?");

gets(p[i].name);

printf("Mobile?");

scanf("%s%*c",p[i].mobile);//%*c用于抑制輸入的回車符

printf("Phone?");

scanf("%s%*c",p[i].phone);//%*c用于抑制輸入的回車符

}}voiddisplay(structTXL*p)//通信錄輸出:結(jié)構(gòu)指針作形參

{inti;for(i=0;i<MAX;i++,p++)printf("%-10s%-12s%-12s\n",p->name,p->mobile,p->phone);}voidmain(){structTXLtx[MAX];

input(tx);//傳遞結(jié)構(gòu)數(shù)組

printf("NameMobilePhone\n");

display(tx);//傳遞結(jié)構(gòu)數(shù)組

}由于結(jié)構(gòu)的單個成員可能是變量、數(shù)組、結(jié)構(gòu)等各種可能,因此,單個結(jié)構(gòu)成員傳遞時,必須區(qū)別對待。根據(jù)上一章和本章的討論,可以得到如下結(jié)論:如果傳遞的成員是變量,則可以用變量名或變量地址傳遞;如果傳遞的成員是一維數(shù)組、二維數(shù)組或指針數(shù)組,則分別采用指針、行指針或二級指針傳遞;如果傳遞的成員是另一個結(jié)構(gòu),則通常采用指針傳遞。(3)單個結(jié)構(gòu)成員的傳遞【例】假設(shè)學(xué)生的三門課成績已保存在一個結(jié)構(gòu)數(shù)組中,要求用自定義函數(shù)計算并返回每個學(xué)生三門課的總分。用score數(shù)組存放三門課成績,并將score作為結(jié)構(gòu)的一個成員被傳遞,此時,用作計算總分的函數(shù)add()只要用指針接收score的首地址即可。

#include<stdio.h>#defineMAX3

structSTUD//定義全局結(jié)構(gòu)類型

{charname[10];intscore[3];inttotal;};

int

add(int*x)//計算并返回總分

{ints=x[0],i;

for(i=1;i<3;i++)s+=x[i];returns; }voidmain(){structSTUDstudent[MAX];//定義結(jié)構(gòu)數(shù)組

inti;for(i=0;i<MAX;i++){printf("Name:");

scanf("%s",student[i].name);//輸入姓名

printf("3course'sScore:");scanf("%d%d%d",&student[i].score[0],&student[i].score[1],&student[i].score[2]);//輸入三門課成績

student[i].total=add(student[i].score);//結(jié)構(gòu)成員的傳遞

}

printf("輸出姓名成績及總分\n");

for(i=0;i<MAX;i++)printf("%-9s%d%d

%d%d\n",student[i].name,

student[i].score[0],student[i].score[1],

student[i].score[2],student[i].total);}被調(diào)用函數(shù)可以通過返回一個結(jié)構(gòu)變量或一個結(jié)構(gòu)指針的方式向調(diào)用函數(shù)傳遞結(jié)構(gòu)型數(shù)據(jù)。返回結(jié)構(gòu)變量的函數(shù)稱為結(jié)構(gòu)型函數(shù),返回結(jié)構(gòu)指針的函數(shù)稱為結(jié)構(gòu)指針型函數(shù)。(1)結(jié)構(gòu)型函數(shù)的返回值定義或說明結(jié)構(gòu)型函數(shù)的一般形式為:

struct

結(jié)構(gòu)標(biāo)識符函數(shù)名(形參表)例如,

structSTUDfunction(int

x,inty){……}就定義了一個structSTUD結(jié)構(gòu)類型的函數(shù),它可以將一個結(jié)構(gòu)變量返回到調(diào)用函數(shù)。2.返回值方式

#include<stdio.h>#include<ctype.h>

structsample{intnum;charcolor;chartype;}car[]={101,'G','C',210,'Y','M',105,'R','L',220,'B','C',308,'W','M',0,'\0','\0'};

structsamplefind(intn)//結(jié)構(gòu)型函數(shù):按編號查找

{inti;for(i=0;car[i].num!=0;i++)if(car[i].num==n)break;

return(car[i]);}【例8】一個汽車檔案中包括汽車的編號、顏色和型號。輸入一個汽車編號,由find()函數(shù)進行查找,根據(jù)查找結(jié)果輸出查找到汽車的信息。voidmain(){intnumber;charch='y';

structsampleresult;

while(toupper(ch)=='Y')//允許反復(fù)查找{printf("輸入編號:");

scanf("%d",&number);result=find(number);//接收結(jié)構(gòu)型函數(shù)返回的結(jié)構(gòu)變量

if(result.num!=0){printf("編號:%d",result.num);

printf("顏色:%c",result.color);

printf("型號:%c\n",result.type);}else

printf("所找編號不存在!\n");

printf("是否繼續(xù)查找(Y/N)?");

scanf("%*c%c",&ch);}}定義或說明結(jié)構(gòu)指針型函數(shù)的一般形式為:

struct

結(jié)構(gòu)名*函數(shù)名(形參表)例如,

structSTUD*function(int

x,inty){……}

定義了一個結(jié)構(gòu)指針型函數(shù)function(),它可以將一個結(jié)構(gòu)指針返回到調(diào)用函數(shù)。(2)結(jié)構(gòu)指針型函數(shù)的返回值【例】將上例程序中的結(jié)構(gòu)型函數(shù)也可以改用結(jié)構(gòu)指針型函數(shù)。

#include<stdio.h>#include<ctype.h>

structsample{intnum;charcolor;chartype;}car[]={101,'G','C',210,'Y','M',105,'R','L',220,'B','C',308,'W','M',0,'\0','\0'};

structsample*find(intn)//結(jié)構(gòu)指針型函數(shù):按編號查找

{inti;for(i=0;car[i].num!=0;i++)if(car[i].num==n)break;

return(&car[i]);}

voidmain(){intnumber;charch='y';

structsample*result;

while(toupper(ch)=='Y') {printf("輸入編號:");

scanf("%d",&number);result=find(number);//接收指針型函數(shù)返回的指針

if(result->num!=0){printf("編號:%d",result->num);

printf("顏色:%c",result->color);

printf("型號:%c\n",result->type);}else

printf("所找編號不存在!\n");

printf("是否繼續(xù)查找(Y/N)?");

scanf("%*c%c",&ch);}}如果一個結(jié)構(gòu)的某些成員與該結(jié)構(gòu)屬于同一類型,稱為遞歸結(jié)構(gòu),也叫自嵌套結(jié)構(gòu)。實踐中用得最多的遞歸結(jié)構(gòu)是:結(jié)構(gòu)的一個成員是指向本結(jié)構(gòu)類型的指針。例如:

structnode{intdata;

structnode*next;};是一個描述節(jié)點的結(jié)構(gòu),由數(shù)據(jù)域和指針域兩部分組成,數(shù)據(jù)域用于存放數(shù)據(jù),指針域用于存放指向下一個節(jié)點的地址。8.3遞歸結(jié)構(gòu)和內(nèi)存動態(tài)分配的綜合應(yīng)用——鏈表的操作8.3.1遞歸結(jié)構(gòu)和鏈表的概念1.遞歸結(jié)構(gòu)鏈表是一種很有用的數(shù)據(jù)結(jié)構(gòu),例如,操作系統(tǒng)使用鏈表結(jié)構(gòu)來將磁盤上若干不連續(xù)的存儲區(qū)鏈接成一片邏輯上連續(xù)的區(qū)域,以便存放較大的文件。單向鏈表是一種最簡單的鏈表,它由若干個節(jié)點首尾相接而成,每個節(jié)點都有數(shù)據(jù)域data和指針域next,如圖所示。2.鏈表的概念頭節(jié)點datanextdatanextdatanextdatanextdataNULL節(jié)點1節(jié)點2節(jié)點3尾節(jié)點單向鏈表中的節(jié)點可用上述遞歸結(jié)構(gòu)來描述。通過指針將各個節(jié)點鏈接起來,就構(gòu)成單向鏈表。根據(jù)各節(jié)點內(nèi)存空間的分配方式,單向鏈表有靜態(tài)鏈表和動態(tài)鏈表之分。靜態(tài)鏈表各個節(jié)點所需的存儲空間是在編譯時安排好的,每個節(jié)點都有自己的名字。例如,下面的定義和語句可以描述一個含三個節(jié)點的靜態(tài)單向鏈表:

structnode{intn;

structnode*next;}a,b,c,*p=&a;

a.next=&b;//或p->next=&b;或(*p).next=&b;

b.next=&c;//或p->next->next=&c;或(*(*p).next).next=&c;

c.next=NULL//或c.next=0;或c.next='\0';在該鏈表中,a為頭節(jié)點,b為中間節(jié)點,c為尾節(jié)點,a、b、c首尾相接。(1)靜態(tài)鏈表動態(tài)鏈表各個節(jié)點所需要的存儲空間是在程序運行時用動態(tài)內(nèi)存分配的方式獲得的,每個節(jié)點都沒有名字,對鏈表的操作只能通過指針進行。下面介紹內(nèi)存的動態(tài)分配方法,然后介紹動態(tài)鏈表的基本操作。(2)動態(tài)鏈表

C語言主要用兩種方法使用內(nèi)存,一種是由編譯系統(tǒng)分配,是在編譯時進行的;另一種是動態(tài)分配,是程序運行時進行的。動態(tài)分配的存儲區(qū)稱為堆區(qū),由用戶在程序中通過動態(tài)分配獲取。使用動態(tài)內(nèi)存分配的優(yōu)點:一是可以更有效地使用內(nèi)存;二是同一段內(nèi)存可作為不同的用途,使用時申請,使用結(jié)束就釋放;三是建立鏈表等動態(tài)數(shù)據(jù)結(jié)構(gòu)。8.3.2內(nèi)存的動態(tài)分配1.內(nèi)存動態(tài)分配的含義①要確切地規(guī)定需要多少內(nèi)存空間,以避免存儲空間的浪費,也可多為其他數(shù)據(jù)留有空間。②利用C編譯系統(tǒng)提供的內(nèi)存動態(tài)分配函數(shù)來分配所需要的存儲空間。③使指針指向獲得的內(nèi)存空間,以便用指針在該空間內(nèi)實施運算或操作。④當(dāng)動態(tài)內(nèi)存用完之后,及時釋放這一空間。如果不釋放獲得的存儲空間,則可能把堆上的內(nèi)存用盡,導(dǎo)致動態(tài)內(nèi)存分配錯誤。2.動態(tài)內(nèi)存分配的步驟

C編譯系統(tǒng)提供了四個內(nèi)存動態(tài)分配函數(shù):calloc()和malloc()用于動態(tài)申請內(nèi)存空間;realloc()用于重新改變已分配的動態(tài)內(nèi)存的大??;free()用于釋放不再使用的動態(tài)內(nèi)存。這四個函數(shù)被定義在標(biāo)題文件stdlib.h中。在程序設(shè)計實踐中常用的是malloc()函數(shù)和free()函數(shù)。

(1)malloc()函數(shù)

malloc()函數(shù)的調(diào)用格式為:

void*malloc(size)

這是一個指針型函數(shù),用來按字節(jié)數(shù)申請內(nèi)存分配。其中,參數(shù)size為unsignedint型,表示申請的字節(jié)數(shù)。如果申請成功,函數(shù)返回一個void型的指針,否則返回NULL指針。

void型指針表示該地址并未指明存放何種類型的數(shù)據(jù),具體到特定問題的使用時,再通過強制類型轉(zhuǎn)換將該地址轉(zhuǎn)換成某種類型存儲單元的地址。3.內(nèi)存動態(tài)分配函數(shù)free()函數(shù)的調(diào)用格式為:

voidfree(p)這是一個無返回值的函數(shù),用來釋放以p為起始地址的動態(tài)內(nèi)存區(qū)。(2)free()函數(shù)【例】編制程序利用動態(tài)內(nèi)存分配存放N個整數(shù)的一維數(shù)組,N的值在程序運行過程中指定,然后從鍵盤輸入任意N個整數(shù)存入該數(shù)組中,并計算其各個元素的平方和。

#include<stdlib.h>#include<stdio.h>voidmain() {int

N,s,i,*p;

printf("輸入數(shù)據(jù)的個數(shù):");

scanf("%d",&N);

if((p=(int*)malloc(N*sizeof(int)))==NULL)//動態(tài)內(nèi)存申請

{printf("動態(tài)內(nèi)存分配失敗.\n");exit(1);//結(jié)束程序運行

}

printf("輸入%d個整數(shù):\n",N);

for(i=0;i<N;i++)

scanf("%d",p+i);s=0;

for(i=0;i<N;i++)s=s+*(p+i)*(*(p+i));//用指針操作動態(tài)內(nèi)存

printf("該%d個數(shù)的平方和:%d\n",N,s);

free(p);//釋放動態(tài)內(nèi)存

}動態(tài)鏈表的主要操作包括建立鏈表、刪除節(jié)點、插入節(jié)點和鏈表的輸出等。8.3.3動態(tài)鏈表的基本操作建立鏈表需要設(shè)置三個指針,h指向鏈表的頭節(jié)點(空節(jié)點:數(shù)據(jù)域為空),p指向尾節(jié)點,q指向當(dāng)前節(jié)點,如圖所示。1.建立鏈表……

nextdatanextdataNULL↑h(頭節(jié)點)↑q(當(dāng)前節(jié)點)↑p(新建尾節(jié)點)鏈表工作指針示意圖創(chuàng)建鏈表的基本過程是:先申請一個頭節(jié)點,然后依次申請新節(jié)點,并鏈接在前一個節(jié)點后,最后申請的新節(jié)點為鏈表尾。算法步驟如下:①通過動態(tài)內(nèi)存分配申請一段存儲空間,將起始地址存放在指針h中,h節(jié)點數(shù)據(jù)域為空,專門存放頭節(jié)點首地址,并作為函數(shù)返回值。由于此時鏈表只有一個節(jié)點,因此它既是頭節(jié)點h,也是當(dāng)前節(jié)點q,又是尾節(jié)點p;②輸入一個整數(shù)a;③若a不為0,則進入④;否則,進入⑥;④申請一個新節(jié)點p,其data域存放整數(shù)a,并通過將其首地址存入q節(jié)點的next域,使之被鏈接在當(dāng)前節(jié)點q之后變成新的尾節(jié)點。再將p節(jié)點首地址存入指針q,使q成為新的當(dāng)前節(jié)點。⑤繼續(xù)輸入下一個整數(shù)a,返回③;⑥結(jié)束循環(huán),并在p節(jié)點的next域存放NULL,作為鏈表結(jié)束標(biāo)記;⑦將鏈表的頭指針h返回調(diào)用函數(shù)?!纠縿?chuàng)建一個鏈表,其每個節(jié)點存放一個非0整數(shù),若輸入0,則創(chuàng)建完畢。

#include<stdio.h>#include<stdlib.h>

structnode{intdata;

structnode*next;};

structnode*creatlist(){structnode*h,*p,*q;

inta;h=(structnode*)malloc(sizeof(structnode));//頭節(jié)點數(shù)據(jù)域為空

p=q=h;//h指向頭節(jié)點,q指向當(dāng)前節(jié)點,p指向尾節(jié)點

scanf("%d",&a);while(a!=0){p=(structnode*)malloc(sizeof(structnode));//產(chǎn)生新節(jié)點pp->data=a;//a存入p節(jié)點數(shù)據(jù)域

q->next=p;//將p節(jié)點鏈接在當(dāng)前節(jié)點q之后,成為新的尾節(jié)點

q=p;//q為新的當(dāng)前節(jié)點

scanf("%d",&a);}p->next=NULL;//鏈表尾

returnh;//返回頭節(jié)點首地址

}voidmain(){structnode*head;head=creatlist();}算法步驟如下:①根據(jù)調(diào)用程序傳遞來的鏈表首地址找到該鏈表的頭節(jié)點,將頭節(jié)點作為當(dāng)前節(jié)點,取其next域中的地址存入指針p;②若p不是NULL,則進入③;否則,進入④;③輸出節(jié)點p的data域數(shù)據(jù),并將該節(jié)點next域中的地址存入指針p,返回②;④返回調(diào)用函數(shù)。

2.輸出鏈表輸出鏈表的基本過程是:從頭節(jié)點開始,順序查找鏈表中的各個節(jié)點,并輸出各節(jié)點數(shù)據(jù)域中的數(shù)據(jù),直到鏈表尾節(jié)點為止。【例】輸出上例創(chuàng)建的鏈表。voidprintlist(structnode*h){structnode*p;p=h->next;//將頭節(jié)點為當(dāng)前節(jié)點,取其next域中的地址存入pwhile(p!=NULL)//循環(huán)到尾節(jié)點

{printf("->%d",p->data);p=p->next;//取當(dāng)前節(jié)點next域中的地址存入p}

printf("\n");}voidmain(){structnode*head;head=creatlist();

printlist(head);}刪除鏈表節(jié)點實質(zhì)上是將待刪除節(jié)點的后繼節(jié)點直接鏈接到它的前驅(qū)節(jié)點上。為此,首先要找到待刪除的節(jié)點p及其前驅(qū)節(jié)點q,然后將p節(jié)點next域中的地址存入q節(jié)點的next域,并釋放p節(jié)點所占的存儲空間。3.刪除鏈表節(jié)點【例】刪除鏈表中的某個節(jié)點。查找節(jié)點p的算法步驟為:①從鏈表頭開始,將頭節(jié)點作為當(dāng)前節(jié)點,將其next域中的地址存入指針p,q為p的前驅(qū)節(jié)點。如果當(dāng)前節(jié)點不是鏈表末尾,其data域值也不是要查找的x,則進入下一步;②循環(huán)順序移動q和p指針,保證q指針在前,p指針在后。直到已達鏈表末尾或已找到其data域值為x的p節(jié)點,退出循環(huán);③如果已到鏈表末尾仍未找到x,就返回NULL指針;如果已找到滿足條件的p節(jié)點,就返回q,即待刪除節(jié)點的前驅(qū)節(jié)點的指針。

structnode*find(structnode*h,intx)//查找數(shù)據(jù)域為x的節(jié)點

{structnode*p,*q;p=h->next;//將頭節(jié)點為當(dāng)前節(jié)點,取其next域中的地址存入pq=h;while(p!=NULL&&p->data!=x){q=p;p=p->next;}if(p==NULL)q=NULL;//未找到待刪節(jié)點,返回NULLreturnq;//返回待刪節(jié)點的前驅(qū)節(jié)點首地址

}刪除節(jié)點p的算法步驟為:①如果頭節(jié)點指針域為NULL,顯示“鏈表為空.”,并返回0;否則,調(diào)用查找函數(shù)find();②若查找函數(shù)find()的返回值q為NULL,則顯示“未找到待刪節(jié)點.”,并返回0;否則,由q節(jié)點找到待刪節(jié)點p,并將待刪節(jié)點p的后繼節(jié)點連接到節(jié)點q的后面,并在釋放p節(jié)點所占的內(nèi)存空間后返回1,如圖所示?!?/p>

nextdatanextdatanextdatanext…↑h(頭節(jié)點)↑q(當(dāng)前節(jié)點)↑p(刪除節(jié)點)刪除鏈表中節(jié)點工作示意圖

int

del(structnode*h,intx)//刪除數(shù)據(jù)域為x的節(jié)點

{structnode*p,*q;if(h->next==NULL){printf("鏈表為空.\n");return0;}q=find(h,x);if(q!=NULL)//待刪除的節(jié)點是否存在

{p=q->next;//p為待刪節(jié)點

q->next=p->next;//將p的后繼節(jié)點作為q節(jié)點的后繼節(jié)點

free(p);//刪除節(jié)點p并釋放它占用的內(nèi)存空間

return1;}else{printf("未找到待刪節(jié)點.\n");return0;}}voidmain(){structnode*head;

intn;head=creatlist();

printlist(head);

printf("輸入待刪節(jié)點的值:");

scanf("%d",&n);

del(head,n);

printlist(head);}算法過程:①申請動態(tài)內(nèi)存存放要插入的節(jié)點s;②從鏈表的頭節(jié)點開始(將q指向頭節(jié)點,p指向下一節(jié)點),不斷順序移動指針q和p,保證q節(jié)點在前,p節(jié)點緊隨其后,直到找到某一節(jié)點p,它要么已到鏈表末尾,要么其data域值已小于x,該節(jié)點就是s節(jié)點的后繼節(jié)點;③將p節(jié)點首地址存入s節(jié)點的next域,將s節(jié)點的首地址存入q節(jié)點的next域。4.在鏈表中插入節(jié)點在鏈表中插入節(jié)點,首先要找到待插節(jié)點的位置p,然后申請新節(jié)點存儲空間,并將新節(jié)點作為p節(jié)點的前驅(qū)節(jié)點,就可以p節(jié)點之前插入新節(jié)點。【例】假設(shè)有一個鏈表,其中各節(jié)點data域中的數(shù)據(jù)已按從大到小的順序排列,要將某個data域值為x的節(jié)點s插入適當(dāng)?shù)奈恢谩!?/p>

nextdatanextdatanextdatanext…↑h(頭節(jié)點)↑q節(jié)點↑p節(jié)點s(插入節(jié)點)鏈表中插入節(jié)點工作示意圖#include<stdio.h>//文件包含說明#include<stdlib.h>//文件包含說明structnode//鏈表結(jié)構(gòu)說明

{intdata;

structnode*next;};structnode*creatlist();//以下為函數(shù)說明voidprintlist();structnode*find();intdel();voidinsert(); voidmain()//主函數(shù)

{structnode*head;

intn;head=creatlist();//創(chuàng)建鏈表

printlist(head);//輸出鏈表

printf("輸入待刪節(jié)點的值:");

scanf("%d",&n);

del(head,n);//刪除鏈表中的節(jié)點

printlist(head);

printf("輸入待插入節(jié)點的值:");

scanf("%d",&n);

insert(head,n);//在鏈表中插入節(jié)點

printlist(head);}voidinsert(structnode*h,intx){structnode*p,*q,*s;s=(structnode*)malloc(sizeof(structnode));

s->data=x;q=h;p=h->next;

while(p!=NULL&&x<p->data){q=p;p=p->next;}s->next=p;q->next=s;}

鏈表頭指針hdatanextdatanextdatanextdatanext鏈表頭節(jié)點鏈表尾節(jié)點單向循環(huán)鏈表示意圖【例】單向循環(huán)鏈表是鏈頭和鏈尾相接的一種鏈表,如圖所示。編一個程序創(chuàng)建具有N個節(jié)點的單向循環(huán)鏈表,并計算該鏈表中每3個相鄰節(jié)點數(shù)據(jù)域中數(shù)值的和,從中找出最小值。

#include<stdlib.h>#defineN8

structnode{intdata;

structnode*next;};main(){structnode*h,*p,*q;

inti,m,m3;/*建立N個節(jié)點的單向循環(huán)鏈表*/p=q=h=(structnode*)malloc(sizeof(structnode));for(i=0;i<N;i++){printf("Inputdata:");

scanf("%d",&p->data);//建立當(dāng)前節(jié)點數(shù)據(jù)域

p=(structnode*)malloc(sizeof(structnode));if(i==(N-1))continue;q->next=p;//保存下一節(jié)點首地址

q=p;}

/*鏈頭和鏈尾相接,形成單向循環(huán)鏈表*/q->next=h;/*p指向鏈頭*/p=h;

/*計算每三個相鄰節(jié)點數(shù)據(jù)值之和,尋找最小值,直至回到鏈頭*/m3=p->data+p->next->data+p->next->next->data;printf("%d\t",m3);//輸出前三個節(jié)點數(shù)據(jù)之和

for(p=p->next;p!=h;p=p->next){m=p->data+p->next->data+p->next->next->data;

printf("%d\t",m);//輸出相鄰三個節(jié)點數(shù)據(jù)之和

if(m3>m)m3=m;}

printf("MIN=%d\n",m3);//輸出最小值}聯(lián)合類型是一種特殊的結(jié)構(gòu)類型,它的最大特點是所有成員共享同一存儲單元。8.4聯(lián)合類型本節(jié)內(nèi)容自學(xué)。8.5位運算和位段結(jié)構(gòu)類型所謂位運算,是對字節(jié)或字節(jié)內(nèi)部的二進制位進行測試、設(shè)置、移位或邏輯運算。本節(jié)內(nèi)容自學(xué)。枚舉類型是列枚舉元素的集合,其中,每個枚舉元素代表一個值。如果一個變量只有有限幾種可能的值,就可以將它定義成枚舉類型變量。所謂“枚舉”是將變量的值一一列舉出來,變量的值只限于列舉值的范圍內(nèi)。因此,枚舉可以看成是C語言中定義符號常量的第三種方法。8.6枚舉類型枚舉類型的定義形式為:

enum

枚舉標(biāo)識符{枚舉元素表}[枚舉變量];其中,enum是關(guān)鍵字,enum連同其后的枚舉標(biāo)識符一起稱為枚舉類型名;花括號中的枚舉元素是一個個由用戶自行定義的標(biāo)識符。例如,

enumcolor{red,yellow,blue,white,black};定義了枚舉類型color,它的枚舉元素依次為red、yellow、blue、white和black。枚舉類型變量或數(shù)組的定義,可以套用8.1節(jié)中有關(guān)結(jié)構(gòu)類型使用的三種方式。1.枚舉類型及枚舉變量的定義枚舉元素是符號常量,它們的值不能用賦值或輸入的方式取得,只能在定義或初始化時獲得。①未進行初始化的枚舉元素,按其在枚舉元素表中的排列次序依次從0開始取值,即第一個元素的值為0,第二個元素的值為1,依此類推。②可以在定義枚舉類型時對枚舉元素初始化,為一個或多個枚舉元素指定希望的值。初始化時,每使用一個初值后,其后的元素依次比它前面的元素大1。也允許多個元素取相同的值。例如

enumposition{east,west=10,south=10,north}loc;則未經(jīng)初始化的east取0值,west初始化為10,south初始化為10,north的值則在south的基礎(chǔ)上遞增1,為11。2.枚舉元素的取值枚舉變量允許進行三種運算:賦值運算、算術(shù)運算和關(guān)系運算。(1)枚舉變量的賦值不能通過鍵盤輸入的方式進行,只能使用下列三種方式:①用枚舉元素給枚舉變量賦值。例如

enumweekday{sun=7,mon=1,tue,wed,thu,fri,sat}workday;workday=sat;則workday的值為6。②同類型的枚舉變量間相互賦值。③用經(jīng)過強制類型轉(zhuǎn)換的整數(shù)值給枚舉變

溫馨提示

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

最新文檔

評論

0/150

提交評論