《C語言程序設(shè)計(jì)》課件2第9章_第1頁
《C語言程序設(shè)計(jì)》課件2第9章_第2頁
《C語言程序設(shè)計(jì)》課件2第9章_第3頁
《C語言程序設(shè)計(jì)》課件2第9章_第4頁
《C語言程序設(shè)計(jì)》課件2第9章_第5頁
已閱讀5頁,還剩153頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡介

第9章構(gòu)造數(shù)據(jù)類型9.1結(jié)構(gòu)體類型9.2共同體類型9.3位段結(jié)構(gòu)類型9.4枚舉類型9.5自定義類型9.6程序舉例習(xí)題9

本章學(xué)習(xí)要求:

1.了解位段結(jié)構(gòu)類型,了解自定義類型的定義方法,理解結(jié)構(gòu)體、共同體類型的定義,掌握結(jié)構(gòu)體變量的定義與使用。

2.掌握結(jié)構(gòu)體數(shù)組的定義與使用,掌握結(jié)構(gòu)體指針的定義與使用,掌握單鏈表的定義、單鏈表的建立及結(jié)點(diǎn)的插入、刪除運(yùn)算。

3.掌握枚舉類型變量的定義與使用。

迄今為止,我們已經(jīng)介紹了C語言中的基本數(shù)據(jù)類型(整型、實(shí)型、字符型)及派生類型(數(shù)組和指針)。但實(shí)際處理問題時,只有這些數(shù)據(jù)類型是不夠的,故C語言給用戶提供了自己聲明數(shù)據(jù)類型的權(quán)利,這些類型稱為構(gòu)造類型。本章將介紹四種構(gòu)造類型:結(jié)構(gòu)體類型、共同體類型、枚舉類型及自定義類型。

有時候我們需要將各種不同數(shù)據(jù)類型的變量、數(shù)組等組合成一個整體,使之相互之間具有一定的聯(lián)系。比如每位學(xué)生個體都具有學(xué)號、姓名、年齡、成績等屬性,而一個班級的學(xué)生在學(xué)號上又具有一定聯(lián)系。要想把這種數(shù)據(jù)結(jié)構(gòu)描述出來,利用目前所學(xué)的知識是無法達(dá)到的,而本章介紹的結(jié)構(gòu)體數(shù)據(jù)類型就可以解決這個問題。9.1結(jié)?構(gòu)?體?類?型9.1.1結(jié)構(gòu)體變量

大家非常清楚,定義一個變量必須指出確定的類型,這樣系統(tǒng)才能為其分配確定大小的存儲空間。而結(jié)構(gòu)體類型是由用戶自己聲明的,所以定義結(jié)構(gòu)體變量之前就必須聲明結(jié)構(gòu)體類型,或者可以在聲明結(jié)構(gòu)體類型的同時定義結(jié)構(gòu)體變量。

1.結(jié)構(gòu)體類型的聲明

結(jié)構(gòu)體類型聲明的一般形式如下:

struct結(jié)構(gòu)體名

{成員列表

};其中:

(1)“struct”是C語言的關(guān)鍵字,是結(jié)構(gòu)體類型的標(biāo)志。

(2)“結(jié)構(gòu)體名”稱為結(jié)構(gòu)標(biāo)記,即代表花括號內(nèi)的聲明,可以用它作為該聲明的簡寫形式,書寫要符合C語言標(biāo)識符命名規(guī)則。

(3)“成員列表”可以是若干個不同類型的變量、數(shù)組。

(4)結(jié)構(gòu)體類型的大小是其所有成員所占空間字節(jié)數(shù)相加之和。

(5)花括號外的分號必不可少。例如:

structdate

{intyear,month,day;};

也可以寫成

structdate

{intyear;

intmonth;

intday;

};以上結(jié)構(gòu)體類型的全稱是structdate,我們用它來描述某一天的日期;它有三個成員項(xiàng)year、month、day,分別代表年、月、日;類型大小是三個整型變量的存儲字節(jié)數(shù)相加之和:6B(字節(jié))。

(6)

結(jié)構(gòu)體類型的聲明還允許嵌套,例如:

structstudent

{intnum;

charname[20];

structdatebirthday;

};

結(jié)構(gòu)體structstudent可以用來描述某個學(xué)生的相關(guān)信息,其中第三個成員birthday本身也是一個結(jié)構(gòu)體類型,稱為嵌套。我們還可以把structdate的聲明嵌套進(jìn)來,形成如下形式:

structstudent

{intnum;

charname[20];

structdate

{intyear,month,day;

}birthday;

};注意,struct聲明定義了一種數(shù)據(jù)類型,只是列出了該數(shù)據(jù)結(jié)構(gòu)的各成員組成情況,即給出了一個數(shù)據(jù)結(jié)構(gòu)的“模版”,系統(tǒng)并沒有為其分配存儲空間,只有用這種類型定義變量或數(shù)組時系統(tǒng)才會為變量或數(shù)組分配存儲空間。對于這一點(diǎn),大家可以這樣理解,“structdate”、“structstudent”與int、float在語法上具有類似的意義,只不過int、float的類型聲明是由系統(tǒng)完成的,對于int、float本身系統(tǒng)并不會為其分配空間。

2.結(jié)構(gòu)體變量的定義

在C語言中,定義結(jié)構(gòu)體變量存在三種方式。

1)在聲明結(jié)構(gòu)體類型之后定義結(jié)構(gòu)體變量

此方式的定義形式如下:

struct結(jié)構(gòu)體名結(jié)構(gòu)體變量名;

若結(jié)構(gòu)體類型的聲明按照前文,則結(jié)構(gòu)體變量的定義如下:

structdatesunday;

structstudentstu1;

這種定義變量的形式與前面我們學(xué)習(xí)過的定義變量的方式相同,并不陌生,因此不作過多闡述。但下面兩種定義變量的形式卻是以前沒有過的。

2)在聲明結(jié)構(gòu)體類型的同時定義結(jié)構(gòu)體變量

此方式的定義形式如下:

struct結(jié)構(gòu)體名

{成員列表

}變量列表;

例如:

structdate

{intyear,month,day;

}sunday;

structstudent

{intnum;

charname[20];

structdatebirthday;

}stu1;

3)在聲明一個無名結(jié)構(gòu)體類型的同時定義結(jié)構(gòu)體變量

此方式的定義形式如下:

struct

{成員列表

}變量列表;

例如:

struct

{intyear,month,day;

}sunday;

struct

{intnum;

charname[20];

structdatebirthday;

}stu1;

這種定義方式的主要特點(diǎn)是聲明的結(jié)構(gòu)體沒有名稱,但是關(guān)鍵字struct必不可少。另外說明一點(diǎn),因?yàn)榇私Y(jié)構(gòu)體沒有名稱,所以不能利用這種結(jié)構(gòu)體像第一種方式那樣去定義變量。獨(dú)立聲明結(jié)構(gòu)體類型的時候是不允許有這種方式的,否則將毫無意義。針對三種定義方式的說明:

(1)變量birthday所占存儲空間為6個字節(jié),變量stu1所占存儲空間為2+20+6=28個字節(jié),其結(jié)構(gòu)如圖9.1所示。

(2)不論哪一種定義方式,都可以同時定義若干個變量,變量名之間用逗號隔開。

圖9.1結(jié)構(gòu)體變量存儲空間示意圖

3.結(jié)構(gòu)體變量的初始化

以上三種方式在定義結(jié)構(gòu)體變量的同時都可以進(jìn)行初始化。下面就以第一種定義結(jié)構(gòu)體變量的方式為例來說明初始化問題。

初始化方法就是將所賦初值按順序放在一對花括號內(nèi),例如:

structdatesunday={2005,6,5};

structstudentstu1={501,"ZhaoLin",1979,10,24};

其存儲結(jié)構(gòu)如圖9.2所示。

圖9.2結(jié)構(gòu)體變量存儲結(jié)構(gòu)示意圖從圖中可以看出,系統(tǒng)是按照從左到右的順序逐一將數(shù)值賦予每個成員的,嵌套結(jié)構(gòu)體變量也是如此。

注意:

(1)不允許對結(jié)構(gòu)體變量獨(dú)立進(jìn)行整體賦值操作,如下形式是不合法的:

sunday={2005,6,5};

(2)所賦初值與各成員數(shù)據(jù)類型要匹配或兼容。

4.結(jié)構(gòu)體變量的使用

老版本的C語言中不允許對結(jié)構(gòu)體變量進(jìn)行整體操作(一種情況例外,見說明第(3)點(diǎn)),只能使用結(jié)構(gòu)體變量的各成員項(xiàng),因此所有的引用操作都必須具體到單個成員項(xiàng)。

1)結(jié)構(gòu)體變量成員的引用

結(jié)構(gòu)體變量成員的引用形式如下:

結(jié)構(gòu)體變量名.成員名

其中,實(shí)心點(diǎn)“.”稱為成員運(yùn)算符。例如:

sunday.yearsunday.daystu1.num說明:

(1)成員運(yùn)算符在C語言中優(yōu)先級最高,與圓括號、下標(biāo)運(yùn)算符同級。

(2)引用內(nèi)嵌結(jié)構(gòu)體變量的成員時,要逐層使用成員運(yùn)算符,例如:

stu1.birthday.monthstu1.birthday.day

(3)允許相同類型的結(jié)構(gòu)體變量之間進(jìn)行賦值運(yùn)算,如下語句是合法的:

structdated1;

d1=sunday;

以上實(shí)現(xiàn)了將結(jié)構(gòu)體變量sunday中各成員的值賦予變量d1種相應(yīng)的成員項(xiàng)。

(4)新的ANSI

C標(biāo)準(zhǔn)允許對結(jié)構(gòu)體變量整體進(jìn)行各種操作。

2)結(jié)構(gòu)體成員的操作

通過以上方法引用到了不可分割的成員項(xiàng),這時每個成員項(xiàng)都屬于確定的我們熟悉的數(shù)據(jù)類型,因此在使用結(jié)構(gòu)體成員項(xiàng)時完全可以把它們當(dāng)作普通的變量或數(shù)組來看待。例如,sunday.year可以看成普通整型變量來操作,可以看成字符數(shù)組來操作,stu1.birthday.day同樣可以看成普通整型變量來操作。

(1)結(jié)構(gòu)體成員的輸入/輸出如下:

scanf(“%d,%d,%s”,&sunday.year,&stu1.birthday.day,);

printf(“%d,%d,%s”,sunday.year,stu1.birthday.day,);

(2)結(jié)構(gòu)體成員的賦值操作如下:

sunday.year=1982;

stu1.birthday.day=11;

類型相同或兼容的結(jié)構(gòu)體成員之間可以相互賦值,例如:

sunday.month=stu1.birthday.day;

實(shí)際應(yīng)用中要注意數(shù)值的合法范圍,如用sunday.month代表月份,那它的取值范圍就是1~12。

(3)結(jié)構(gòu)體成員的一般運(yùn)算如下:

相同類型的普通變量能夠進(jìn)行的運(yùn)算,結(jié)構(gòu)體成員也可以進(jìn)行,例如:

sunday.year++;

sunday.month=stu1.birthday.day-2;

同樣可以進(jìn)行關(guān)系、邏輯等運(yùn)算,例如:

(sunday.month>=1)&&(sunday.month<=12)

!(sunday.year)

3)結(jié)構(gòu)體變量作函數(shù)參數(shù)

(1)結(jié)構(gòu)體變量的成員作函數(shù)參數(shù)。結(jié)構(gòu)體變量中的每個成員可以是普通變量、指針變量、數(shù)組等,它們既然可以參與上文所述的若干運(yùn)算,當(dāng)然也可以作為函數(shù)參數(shù)來傳遞。若實(shí)參是結(jié)構(gòu)體變量成員,則形參可以是與成員子類型相同或兼容的普通變量。例如:

struct

{intyear,month,day;

}sunday;

intage(intx) /*定義子函數(shù),整型變量作形參*/

{……}

main()

{…

age(sunday.year);

/*調(diào)用子函數(shù),結(jié)構(gòu)體變量成員作實(shí)參*/

}

(2)結(jié)構(gòu)體變量作函數(shù)參數(shù)。結(jié)構(gòu)體變量作函數(shù)參數(shù)傳遞的是所有成員的數(shù)據(jù),因此實(shí)參與形參必須是相同類型的結(jié)構(gòu)體變量。例如:

structdate

{intyear,month,day;

};

intage(structdatemy) /*定義子函數(shù),結(jié)構(gòu)體變量作形參*/

{……}

main()

{structdatefy;

age(fy); /*調(diào)用子函數(shù),結(jié)構(gòu)體變量作實(shí)參*/

}

則函數(shù)調(diào)用時實(shí)參結(jié)構(gòu)體變量fy將其三個成員項(xiàng)的值傳給了形參結(jié)構(gòu)體變量my相應(yīng)的三個成員項(xiàng)。

例9.1

編寫程序,求解某一點(diǎn)在平面坐標(biāo)中關(guān)于原點(diǎn)的對稱點(diǎn)。

平面坐標(biāo)系中一般用(x,y)來表示一個點(diǎn),且x和y是有關(guān)聯(lián)的,是一個整體。因此我們可以用具有兩個成員項(xiàng)的結(jié)構(gòu)體來表示這種結(jié)構(gòu),這里假設(shè)x、y的坐標(biāo)值均為整數(shù)。而點(diǎn)(x,y)關(guān)于原點(diǎn)的對稱點(diǎn)應(yīng)該是(-x,-y),程序如下:

#include<stdio.h>

structpoint

{intx;

inty;

};

main()

{inta,b;

structpointpi;

printf("Pleaseentertwonumbers:\n");

scanf("%d,%d",&pi.x,&pi.y);

a=-pi.x;

b=-pi.y;

printf("symmetricalpoint:%d,%d\n",a,b);

}

若利用函數(shù)實(shí)現(xiàn),則程序如下:

#include<stdio.h>

structpoint

{intx;

inty;

};

voidspoint(inta,intb)

{a=-a;

b=-b;

printf("symmetricalpoint:%d,%d\n",a,b);

}

main()

{structpointpi;

printf("Pleaseentertwonumbers:\n");

scanf("%d,%d",&pi.x,&pi.y);

spoint(pi.x,pi.y);

}9.1.2結(jié)構(gòu)體數(shù)組

一個結(jié)構(gòu)體變量只能表示一個實(shí)體的信息,例如一個“structstudent”類型的結(jié)構(gòu)體變量只能表示一個學(xué)生的信息,如果想把存在關(guān)聯(lián)的一組學(xué)生信息存放在一起則需要使用結(jié)構(gòu)體數(shù)組。

1.結(jié)構(gòu)體數(shù)組的定義和初始化

通過第6章的學(xué)習(xí),我們能夠推出,結(jié)構(gòu)體數(shù)組的含義就是它的每個元素都是結(jié)構(gòu)體類型的,這也是它與一般數(shù)值型數(shù)組的唯一不同之處。

1)結(jié)構(gòu)體數(shù)組的定義

結(jié)構(gòu)體數(shù)組的定義方法與結(jié)構(gòu)體變量的定義方法類似,存在三種形式,此處就不再一一詳細(xì)講解。下面給出第一種定義形式:

struct結(jié)構(gòu)體名結(jié)構(gòu)體數(shù)組名[整型表達(dá)式];

例如:

structdatedt[4];

structstudentst[30];

2)結(jié)構(gòu)體數(shù)組的初始化

結(jié)構(gòu)體數(shù)組的初始化同第6章中其他類型數(shù)組的初始化類似。但由于結(jié)構(gòu)體數(shù)組的每個元素都是包含若干成員的結(jié)構(gòu)體,因此結(jié)構(gòu)體數(shù)組初始化時,通常在每個元素的兩邊加一對花括號,以便區(qū)分清楚數(shù)組的各個元素,每對花括號之間用逗號隔開。例如:

structdatedt[4]={{1982,7,3},{1980,12,5},{1981,9,15},{1980,3,11}};

可以看到,數(shù)組dt有四個元素:dt[0]、dt[1]、dt[2],dt[3],每個元素都有三個成員項(xiàng),每個元素都占6個字節(jié),因此整個數(shù)組共占存儲空間24個字節(jié)。又例如:

structworker

{intnum;

charname[20];

floatsalary;

}warr[3]={{9901,"Zhaolin",82.5},{9902,"Lifei",75},{9903,"Chenjun",91.5}};

可以看到,數(shù)組warr有三個元素:warr[0]、warr[1]、warr[2],每個元素都有三個成員項(xiàng),每個元素都占26個字節(jié),因此系統(tǒng)共分配了78個字節(jié)的連續(xù)存儲空間給數(shù)組warr。說明:

(1)當(dāng)初始化元素個數(shù)與定義的數(shù)組長度相等時,可以省略數(shù)組長度。例如:

structdatedt[]={{1982,7,3},{1980,12,5},{1981,9,15},{1980,3,11}};

(2)元素、元素內(nèi)成員項(xiàng)均可以進(jìn)行部分初始化,系統(tǒng)按照從左到右的順序賦值。

例如:

structdatedt[4]={{1982},{1980,12,5},{1981,9,15}};

該語句沒有給元素dt[3]賦初值,它的三個成員項(xiàng)的值均為0,而給第一個元素dt[0]也只提供了一個數(shù)據(jù),則后兩個成員項(xiàng)的值也為0。

(3)除了初始化時可以對結(jié)構(gòu)體數(shù)組整體賦初值,其他任何情況下都不能對結(jié)構(gòu)體數(shù)組進(jìn)行整體賦值,如下語句均是錯誤的:

warr[3]={{9901,"Zhaolin",82.5},{9902,"Lifei",75},{9903,"Chenjun",91.5}};

warr={{9901,"Zhaolin",82.5},{9902,"Lifei",75},{9903,"Chenjun",91.5}};

2.結(jié)構(gòu)體數(shù)組的使用

在講述結(jié)構(gòu)體變量時,我們說過操作時要具體到單個成員項(xiàng),對結(jié)構(gòu)體數(shù)組的使用也同樣要遵循這個原則(例外情況見說明)。

1)結(jié)構(gòu)體數(shù)組的引用

結(jié)構(gòu)體數(shù)組的引用形式如下:

結(jié)構(gòu)體數(shù)組名[數(shù)組下標(biāo)].成員名

若有定義語句structdatedt[4];和structwokerwarr[10];,則以下引用形式均是合法的:

dt[0].year,dt[0].month,dt[0].day,dt[3].day

warr[1].num,warr[2].name,warr[3].name,warr[8].salary

說明:相同結(jié)構(gòu)體類型的數(shù)組元素之間可以相互賦值,例如:

dt[0]=dt[1];

warr[7]=warr[1];

這是結(jié)構(gòu)體數(shù)組元素之間可以進(jìn)行整體操作的唯一一種運(yùn)算。

2)結(jié)構(gòu)體數(shù)組的操作

同以前學(xué)過的數(shù)組操作類似,因?yàn)閿?shù)組下標(biāo)是有規(guī)律變化的,所以結(jié)構(gòu)體數(shù)組的操作也通常是利用循環(huán)對各個元素逐一進(jìn)行控制。但與其他類型數(shù)組的不同之處就是,每個元素還要細(xì)化到成員項(xiàng)。當(dāng)細(xì)化到成員項(xiàng)之后就相當(dāng)于使用相同類型的普通變量或數(shù)組,此處僅以輸入和輸出作為示例。

結(jié)構(gòu)體數(shù)組的輸入和輸出:

(1)

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

scanf("%d,%d,%d",&dt[i].year,&dt[i].month,&dt[i].day);

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

printf("%d,%d,%d\n",dt[i].year,dt[i].month,dt[i].day);

(2)

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

scanf("%d,%s,%f",&warr[i].num,warr[i].name,&warr[i].salary);

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

printf("%d,%s,%f",warr[i].num,warr[i].name,warr[i].salary);

scanf()語句中,因?yàn)閣arr[i].name是數(shù)組,數(shù)組名代表數(shù)組起始地址,因此不應(yīng)再加取地址"&"運(yùn)算符。

3)結(jié)構(gòu)體數(shù)組作函數(shù)參數(shù)

結(jié)構(gòu)體數(shù)組作函數(shù)參數(shù)傳遞的是地址,若實(shí)參是結(jié)構(gòu)體數(shù)組,則形參可以是結(jié)構(gòu)體數(shù)組也可以是結(jié)構(gòu)體指針,這部分內(nèi)容在下一節(jié)中再詳細(xì)講解。

例9.2

假設(shè)描述個體的結(jié)構(gòu)體如下,則編寫程序求分?jǐn)?shù)最高者。要求從鍵盤輸入數(shù)據(jù),然后輸出得分最高者的相關(guān)信息。

#include<stdio.h>

structperson

{longsn;

charname[20];

floatscore;

}pe[50];

main()

{inti,j;

floatmax;

printf("Pleaseentername,snandscore:\n");

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

{gets(pe[i].name);

scanf("%ld,%f",&pe[i].sn,&pe[i].score);

}

max=pe[0].score;j=0;

for(i=1;i<50;i++)

if(pe[i].score>max)

{max=pe[i].score;

j=i;

}

printf("Theresultis:\n");

printf("%d,%f,%s",pe[j].sn,pe[j].score,pe[j].name);

}程序說明:

(1)用scanf()函數(shù)輸入字符串遇空格即結(jié)束,不能滿足實(shí)際要求,所以我們采用gets()函數(shù)輸入字符串,它只遇換行符結(jié)束。

(2)此例題并不僅僅是統(tǒng)計(jì)分?jǐn)?shù)最高者,還要求最后輸出其相關(guān)信息,因此必須記錄其位置才能解決問題,引入變量j即是為了記錄具有最高分?jǐn)?shù)元素的下標(biāo)。9.1.3*結(jié)構(gòu)體指針

通過第7章的學(xué)習(xí),我們知道指針即是地址,而結(jié)構(gòu)體的指針即是結(jié)構(gòu)體的地址,包括結(jié)構(gòu)體變量的指針和結(jié)構(gòu)體數(shù)組的指針。

1.結(jié)構(gòu)體指針變量的定義

與定義結(jié)構(gòu)體變量、數(shù)組的方法類似,結(jié)構(gòu)體指針變量的定義也有三種形式。

1)在聲明結(jié)構(gòu)體類型之后定義結(jié)構(gòu)體指針變量

此方式的定義形式如下:

struct結(jié)構(gòu)體名*指針變量名;

例如:

structdate*psd;

structworker*pwk;

這種定義與第7章中我們學(xué)習(xí)的定義指針變量的方式類似,注意變量名前的“*”號,它起標(biāo)識的作用,但不包括在變量名之內(nèi)。

2)在聲明結(jié)構(gòu)體類型的同時定義結(jié)構(gòu)體指針變量

此方式的定義形式如下:

struct結(jié)構(gòu)體名

{成員列表

}*指針變量名;

例如:

structdate

{intyear,month,day;

}*psd;

structworker

{intnum;

charname[20];

floatsalary;

}*pwk;

3)在聲明一個無名結(jié)構(gòu)體類型的同時定義結(jié)構(gòu)體變量

此方式的定義形式如下:

struct

{成員列表

}*指針變量名;

例如:

struct

{intyear,month,day;

}*psd;

struct

{intnum;

charname[20];

floatsalary;

}*pwk;

以上定義的結(jié)構(gòu)體指針psd、pwk均可以指向結(jié)構(gòu)體類型相同的結(jié)構(gòu)體變量或結(jié)構(gòu)體數(shù)組。

2.指向結(jié)構(gòu)體變量的指針

若有以下語句:

structworker

{intnum;

charname[20];

floatsalary;

}wk,*pwk;

pwk=&wk;

則這時指針pwk是指向變量wk的,即通過pwk可以實(shí)現(xiàn)操作wk的目的。我們知道,引用指針?biāo)赶虻淖兞康姆椒ㄊ抢弥羔樳\(yùn)算符“*”號,但在結(jié)構(gòu)體中還必須具體到單個成員項(xiàng),用到成員運(yùn)算符“.”號。因此通過指針引用結(jié)構(gòu)體變量中的成員有如下兩種方式:

(*指針變量名).成員名

指針變量名->成員名

其中出現(xiàn)了新的運(yùn)算符“->”,稱為指向運(yùn)算符,它由減號“-”和大于號“>”兩部分組成,中間不得有空格,它的優(yōu)先級同圓括號、成員運(yùn)算符一樣也是最高級。

另外,第一種方式中的圓括號必不可省,否則會產(chǎn)生錯誤,因?yàn)槌蓡T運(yùn)算符的優(yōu)先級高于指針運(yùn)算符。

例如,利用指針對結(jié)構(gòu)體變量輸入和輸出:

gets((*pwk).name);

scanf("%d%f",&(*pwk).num,&(*pwk).salary);

printf("%d,%f,%s",pwk->num,pwk->salary,pwk->name);

因?yàn)橹赶蜻\(yùn)算符“->”方便、直觀,所以使用較多

3.指向結(jié)構(gòu)體數(shù)組的指針

若有以下語句:

structworker

{intnum;

charname[20];

floatsalary;

}warr[20],*pwa;

pwa=warr;/*或,pwa=&warr[0];*/則這時指針pwa指向數(shù)組warr的起始位置,即通過pwa可以實(shí)現(xiàn)操作數(shù)組warr元素的目的。利用指針運(yùn)算符“*”號可以引用數(shù)組元素,但每個結(jié)構(gòu)體數(shù)組元素還包含若干成員,因此也必須具體到單個成員項(xiàng)。而每個結(jié)構(gòu)體數(shù)組元素就相當(dāng)于同類型的變量,所以通過指針引用結(jié)構(gòu)體數(shù)組元素中的成員也存在如下兩種方式:

(*指針變量名).成員名

指針變量名->成員名

例如,(*pwa).num和pwa->num,即是warr[0].num。

通過前面第7章的學(xué)習(xí),我們知道可以使用指針通過循環(huán)來引用數(shù)組的每個元素,在結(jié)構(gòu)體數(shù)組中也同樣可以。例如:

for(pwa=warr;pwa<warr+20;pwa++)

{gets(pwa->name);

scanf("%d%f",&pwa->num,&pwa->salary);

}

for(pwa=warr;pwa<warr+20;pwa++)

printf("%d,%f,%s",pwa->num,pwa->salary,pwa->name);說明:

(1)對指針進(jìn)行“++”運(yùn)算是使指針指向下一個數(shù)組元素,而絕不是指向下一個存儲單元。例如,“pwa++”表示指針移動了26個存儲單元而指向下一結(jié)構(gòu)體數(shù)組元素。

(2)編譯系統(tǒng)不會做越界檢查,所以要注意終止條件。

(3)表達(dá)式++pwa->num等價于++(pwa->num),所以是使得num加1,因?yàn)橹赶蜻\(yùn)算符的優(yōu)先級高于自加運(yùn)算;若想使pwa加1,應(yīng)寫成(++pwa)->num,則pwa先自加指向下一數(shù)組元素,然后再取num成員;另外,(pwa++)->num與pwa++->num等價,在訪問pwa所指的num成員之后再自加指向下一元素。

4.結(jié)構(gòu)體指針作函數(shù)參數(shù)

1)結(jié)構(gòu)體變量指針作函數(shù)參數(shù)

若結(jié)構(gòu)體變量指針作函數(shù)形參,則實(shí)參可以是結(jié)構(gòu)體變量指針,也可以是結(jié)構(gòu)體變量地址。

例9.3編寫函數(shù),利用指針給結(jié)構(gòu)體變量輸入數(shù)據(jù)。

#include<stdio.h>

structworker

{intnum;

charname[20];

floatsalary;

};

voidgetdata(structworker*pwk)

{printf("Pleaseentername,snandscore:\n");

gets(pwk->name);

scanf("%d%f",&pwk->num,&pwk->salary);

}

main()

{structworkerwk;

getdata(&wk); /*結(jié)構(gòu)體變量地址作函數(shù)實(shí)參*/

printf("%d,%f,%s",wk.num,wk.salary,);

}若采用結(jié)構(gòu)體指針作函數(shù)實(shí)參,則主函數(shù)修改如下:

main()

{structworkerwk,*p;

p=&wk;

getdata(p); /*結(jié)構(gòu)體變量指針作函數(shù)實(shí)參*/

printf("%d,%f,%s",p->num,p->salary,p->name);

}

注意,這種情況下定義一個結(jié)構(gòu)體變量是必須的,否則指針的內(nèi)容是空的,它無法作為實(shí)參來使用。

2)結(jié)構(gòu)體數(shù)組指針作函數(shù)參數(shù)

若結(jié)構(gòu)體數(shù)組指針作函數(shù)參數(shù),則實(shí)參與形參應(yīng)該都是地址。又因?yàn)閿?shù)組名本身就代表數(shù)組的起始地址,所以實(shí)參與形參可以出現(xiàn)的組合有四種,如表9.1所示。

表9.1結(jié)構(gòu)體數(shù)組作函數(shù)時的形參與實(shí)參的四種組合下面我們以第三種情況為例進(jìn)行說明。

例9.4

編寫函數(shù),利用指針給結(jié)構(gòu)體數(shù)組輸入數(shù)據(jù)。

#include<stdio.h>

structworker

{intnum;

charname[20];

floatsalary;

};

voidgetdata(structworker*pak)

{structworker*p;

for(p=pak;p<pak+20;p++)

{gets(p->name);

scanf("%d%f",&p->num,&p->salary);

}

}

main()

{structworkerwarr[20],*q=warr;

getdata(warr);/*還可以是,getdata(q)*/

for(;q<warr+20;q++)

printf("%d,%f,%s",q->num,q->salary,q->name);

}9.1.4*單鏈表

1.單鏈表的概念及結(jié)構(gòu)

到目前為止,大家知道要存儲一組有關(guān)聯(lián)的數(shù)據(jù)需利用數(shù)組,比如一個班的學(xué)生、一個教研室的老師、一個車間的工人等等。通過第8章函數(shù)的學(xué)習(xí)可知,對于常用算法,我們通常把它做成一個模塊(函數(shù))以便反復(fù)調(diào)用,而每次調(diào)用時數(shù)據(jù)的個數(shù)及大小通常都會發(fā)生變化,如果采用數(shù)組來存儲數(shù)據(jù),則定義時我們必須按照最大可能的情況來確定數(shù)組長度,所以很多空間就會浪費(fèi)。另外,數(shù)組中的插入、刪除等操作也非常麻煩,通常需要移動較多元素。

而鏈表就可以解決以上兩個問題,它的結(jié)構(gòu)圖如圖9.3和圖9.4所示。

圖9.3單鏈表示意圖

圖9.4空鏈表示意圖由圖9.3可以看出,鏈表由一個頭指針及若干結(jié)點(diǎn)組成。每個結(jié)點(diǎn)由數(shù)據(jù)域和指針域兩部分組成,數(shù)據(jù)域存放描述個體的相關(guān)信息,指針域存放的是下一個結(jié)點(diǎn)的地址。頭指針指向鏈表的第一個結(jié)點(diǎn),鏈表可以沒有頭結(jié)點(diǎn)(見圖9.4(b)),頭結(jié)點(diǎn)中不存放數(shù)據(jù)。尾結(jié)點(diǎn)是最后一個結(jié)點(diǎn),它不指向任何地方,所以指針域的地址是空值,在C語言中用'\0'(NULL)表示。結(jié)點(diǎn)的空間是在程序執(zhí)行過程中根據(jù)需要隨時向系統(tǒng)申請開辟的存儲單元,不需要時又能隨時釋放結(jié)點(diǎn)、釋放存儲空間,如此按需分配則不存在空間浪費(fèi)的問題。各個結(jié)點(diǎn)在內(nèi)存中的位置也不是連續(xù)的,對空間的要求也相應(yīng)降低,它們之間是通過指針建立起來的前后次序關(guān)系,因此插入、刪除等操作只要改變個別指針的指向即可,無需移動任何結(jié)點(diǎn)。由于這種鏈表中每個結(jié)點(diǎn)都沒有名稱,只能從頭指針開始由前往后逐個查找訪問,即只存在一種訪問鏈表的次序(或方向),因此稱為“單向鏈表”,也稱“單鏈表”。

既然一個結(jié)點(diǎn)實(shí)體至少需要兩個部分:數(shù)據(jù)域和指針域,因此描述它的類型應(yīng)該是結(jié)構(gòu)體類型,又因?yàn)檫@種指針指向的仍是相同類型的結(jié)點(diǎn),所以指針的基類型應(yīng)是自身結(jié)構(gòu)體類型。鏈表結(jié)構(gòu)體類型聲明如下:

structlink

{intdata;

structlink*next; /*引用自身的結(jié)構(gòu)體指針變量*/

};

2.靜態(tài)鏈表

在以上介紹的單鏈表中我們說過,結(jié)點(diǎn)的申請和釋放都是在程序執(zhí)行過程中動態(tài)進(jìn)行的,所以又稱為動態(tài)鏈表。其實(shí),還存在一些簡單的鏈表,它具有鏈表的結(jié)構(gòu)特征,但它的結(jié)點(diǎn)是定義好的靜態(tài)結(jié)構(gòu)體變量,結(jié)點(diǎn)的個數(shù)在程序執(zhí)行前也都是確定的,總之鏈表是通過固定語句“手工”搭建起來的,因此稱為靜態(tài)鏈表。例9.5

建立靜態(tài)鏈表。

#include<stdio.h>

structslink

{intdata;

structslink*next;

};

main()

{structslinkx,y,z,*head,*q;

x.data=10;y.data=11;z.data=12; /*給數(shù)據(jù)域賦值*/

head=&x;

x.next=&y;

y.next=&z;

z.next=’\0’;

}

如此建立的鏈表結(jié)構(gòu)如圖9.5所示。

圖9.5靜態(tài)鏈表結(jié)構(gòu)

3.動態(tài)單鏈表

本節(jié)重點(diǎn)學(xué)習(xí)動態(tài)單鏈表,以下簡稱單鏈表。學(xué)習(xí)內(nèi)容包括單鏈表的建立、訪問、插入、刪除等基本操作。

1)處理動態(tài)鏈表所需的函數(shù)

(1)

malloc()函數(shù)。

函數(shù)原型如下:

void*malloc(unsignedintsize);

函數(shù)功能:在內(nèi)存的動態(tài)存儲區(qū)中分配一個長度為size的連續(xù)空間。

函數(shù)值:如果成功,返回的是分配存儲區(qū)的起始地址;如果不成功,返回的是空地址(NULL)。因此我們通常把函數(shù)值賦予一個指針變量。說明:

①由于鏈表結(jié)構(gòu)體類型中存在指針變量成員,其類型大小很難確定,因此我們通常是采用系統(tǒng)提供的運(yùn)算符sizeof()來求解鏈表結(jié)構(gòu)體類型長度。例如:

sizeof(structlink)

②函數(shù)返回值的基類型是void,而實(shí)際應(yīng)用中均會有確定類型,因此需利用強(qiáng)制類型轉(zhuǎn)換運(yùn)算符“()”進(jìn)行類型轉(zhuǎn)換。例如:

structlink

{intdata;

structlink*next;

}*q;

q=(structlink*)malloc(sizeof(structlink));

以上語句的功能就是向系統(tǒng)申請了一塊structlink類型大小的區(qū)域,并將起始地址賦予了指針變量q,其實(shí)在單鏈表中這塊區(qū)域就是一個結(jié)點(diǎn),稱為結(jié)點(diǎn)q。

(2)

free()函數(shù)。

函數(shù)原型如下:

voidfree(void*p);

函數(shù)功能:釋放由指針變量p所指向的內(nèi)存區(qū)域,將區(qū)域使用權(quán)交還給系統(tǒng)。

說明:

①此函數(shù)無返回值。

p必須是調(diào)用malloc函數(shù)返回的值,不能妄圖用此函數(shù)釋放所有指針指向的地址。

2)單鏈表的建立

建立動態(tài)鏈表的過程就是,在程序執(zhí)行過程中一個一個地開辟空間,生成新結(jié)點(diǎn),然后再將結(jié)點(diǎn)一個個連接起來。大致步驟如下:

(1)讀取數(shù)據(jù),判斷是否有效,若是有效數(shù)據(jù)則執(zhí)行步驟(2),若無效則不再讀取數(shù)據(jù)。

(2)申請新結(jié)點(diǎn)。

(3)將數(shù)據(jù)存入結(jié)點(diǎn)的成員變量中。

(4)將新結(jié)點(diǎn)插入到鏈表末尾。

(5)重復(fù)步驟(1)~(4)。

例9.6

編一函數(shù),建立帶頭結(jié)點(diǎn)的單鏈表,要求數(shù)據(jù)從鍵盤輸入直至遇-1結(jié)束。

說明:本程序中,h是頭指針,指向頭結(jié)點(diǎn);new用來指向新生成的結(jié)點(diǎn);而rear則始終指向當(dāng)前鏈表的尾結(jié)點(diǎn)。

#include<stdio.h>

#defineLENsizeof(structlink)

structlink

{intdata;

structlink*next;

};

structlink*creat_link()

{inta;

structlink*h,*new,*rear;

h=(structlink*)malloc(LEN); /*生成頭結(jié)點(diǎn)*/

rear=h;

scanf("%d",&a); /*讀入數(shù)據(jù)*/

while(a!=-1)

{new=(structlink*)malloc(LEN); /*申請新結(jié)點(diǎn)*/

new->data=a; /*存放數(shù)據(jù)*/

rear->next=new; /*將新結(jié)點(diǎn)連接到表尾*/

rear=new; /*使rear指向當(dāng)前表尾*/

scanf("%d",&a); /*讀入數(shù)據(jù)*/

}

rear->next='\0'; /*置最終的表尾結(jié)點(diǎn)指針域?yàn)榭?/

returnh; /*返回頭指針*/

}

main()

{structlink*head;

head=creat_link(); /*調(diào)用建立子函數(shù)*/

}

運(yùn)行程序時,若輸入:

102030-1

則形成的鏈表如圖9.6所示。

圖9.6建成的單鏈表

3)單鏈表的訪問

鏈表建立好之后,就會經(jīng)常對其進(jìn)行各種各樣的訪問。而鏈表與數(shù)組完全不同,它各個結(jié)點(diǎn)的存儲是不連續(xù)的,因此就不能像數(shù)組那樣通過下標(biāo)的變化來控制訪問。在鏈表中不管進(jìn)行什么運(yùn)算,包括插入和刪除,都必須從頭指針開始,通過每個結(jié)點(diǎn)的指針域一個一個向下訪問,不能進(jìn)行直接定位。

例9.7

編一函數(shù),順序輸出帶頭結(jié)點(diǎn)的單鏈表各結(jié)點(diǎn)的數(shù)據(jù)域內(nèi)容。

voidprint_link(structlink*head)

{structlink*q;

q=head->next; /*讓q指向第一個有效數(shù)據(jù)結(jié)點(diǎn)*/

if(q=='\0') /*鏈表為空*/

printf("linklistisnull\n");

else

{do

{printf("%5d",q->data); /*輸出結(jié)點(diǎn)數(shù)據(jù)*/

q=q->next; /*讓q指向下一個結(jié)點(diǎn)*/

}while(q!='\0');

}

}

4)單鏈表的插入操作

很多時候需要將一個新結(jié)點(diǎn)插入到鏈表的某個位置。如果在某結(jié)點(diǎn)之前插入一個新結(jié)點(diǎn),則稱為“前插”法,如果在某結(jié)點(diǎn)之后插入一個新結(jié)點(diǎn),則稱為“后插”法。下面給出在結(jié)點(diǎn)q和m之間插入新結(jié)點(diǎn)new的示意圖9.7。

圖9.7單鏈表中結(jié)點(diǎn)插入示意圖

例9.8

在帶頭結(jié)點(diǎn)的單鏈表中,編一函數(shù),在值為x的結(jié)點(diǎn)之后插入值為y的結(jié)點(diǎn),若值為x的結(jié)點(diǎn)不存在則插在表尾。

解決這個問題,我們首先要查找x結(jié)點(diǎn),再進(jìn)行插入操作。則可能出現(xiàn)的插入情況有如下三種,程序應(yīng)該全面處理:

(1)鏈表非空,值為x的結(jié)點(diǎn)存在,新結(jié)點(diǎn)插在此結(jié)點(diǎn)之后。

(2)鏈表非空,值為x的結(jié)點(diǎn)不存在,新結(jié)點(diǎn)插在表尾。

(3)鏈表為空,則新結(jié)點(diǎn)應(yīng)插入在頭結(jié)點(diǎn)之后,成為第一個結(jié)點(diǎn)。程序如下:

structlink*insert_link(structlink*head,intx,inty)

{structlink*new,*q;

new=(structlink*)malloc(LEN); /*生成新結(jié)點(diǎn)*/

new->data=y; /*新結(jié)點(diǎn)中存入數(shù)據(jù)y*/

q=head;

while((q->next!='\0')&&(q->data!=x)) /*表非空且未到表尾,查找值為x的結(jié)點(diǎn)*/

q=q->next; /*q不斷向后移動*/

new->next=q->next; /*讓新結(jié)點(diǎn)指向q之后的結(jié)點(diǎn)*/

q->next=new; /*讓q指向新結(jié)點(diǎn)*/

return(head);

}函數(shù)執(zhí)行時,若是空表,則循環(huán)的第一個條件(q->next!='\0')不滿足,循環(huán)不進(jìn)行,此時q指向頭結(jié)點(diǎn),則執(zhí)行語句“new->next=q->next;q->next=new;”,便會使new成為第一個結(jié)點(diǎn);若不是空表且值為x的結(jié)點(diǎn)存在,則通過循環(huán)會使q指向值為x的結(jié)點(diǎn),再執(zhí)行語句“new->next=q->next;q->next=new;”,使new結(jié)點(diǎn)插入在q結(jié)點(diǎn)之后;若不是空表且值為x的結(jié)點(diǎn)不存在,則通過循環(huán)會使q指向尾結(jié)點(diǎn),再執(zhí)行語句“new->next=q->next;q->next=new;”,使new結(jié)點(diǎn)插入尾結(jié)點(diǎn)之后。

讀者可以思考:如何實(shí)現(xiàn)在值為x的結(jié)點(diǎn)之前插入值為y的結(jié)點(diǎn)?

5)單鏈表的刪除操作

有些時候需要將鏈表中的某個結(jié)點(diǎn)刪除,主要操作就是讓待刪結(jié)點(diǎn)之前的結(jié)點(diǎn)指向其后的結(jié)點(diǎn),這樣再從head開始訪問鏈表時就找不到了。上述過程稱為“邏輯”刪除,因?yàn)樵摻Y(jié)點(diǎn)所占的存儲空間并沒有釋放掉;只有用free()函數(shù)將其空間釋放,才是真正的刪除,稱為“物理”刪除。

例9.9

在帶頭結(jié)點(diǎn)的單鏈表中,編一函數(shù)刪除指定結(jié)點(diǎn)。

假定被刪除結(jié)點(diǎn)是m,如圖9.8所示,我們必須找到m之前的結(jié)點(diǎn)才能實(shí)現(xiàn)刪除操作。

圖9.8單鏈表中結(jié)點(diǎn)刪除示意圖

structlink*delete_link(structlink*head,intx)

{structlink*q,*m;

q=head;

m=head->next;

while((m!='\0')&&(m->data!=x)) /*尋找被刪除結(jié)點(diǎn)m*/

{q=m; /*q始終指向m之前的結(jié)點(diǎn)*/

m=m->next;

}

if(m=='\0') /*不存在符合條件的結(jié)點(diǎn)*/

printf("cannotexit!\n");

else

{q->next=m->next; /*邏輯刪除結(jié)點(diǎn)*/

free(m); /*物理刪除結(jié)點(diǎn)*/

}

return(head);

}

因?yàn)楸纠}處理的鏈表是帶頭結(jié)點(diǎn)的,所以即使被刪除結(jié)點(diǎn)是頭結(jié)點(diǎn)之后的第一個結(jié)點(diǎn)也不需要作特殊處理。但如果鏈表不帶頭結(jié)點(diǎn),則出現(xiàn)此種情況時就得考慮對head的修改問題。

共同體也是一種構(gòu)造數(shù)據(jù)類型,它的主要特點(diǎn)是,共同體變量中的所有成員占用同一段存儲空間,這段空間的大小就是所有成員中字節(jié)數(shù)最大的值。而我們學(xué)習(xí)的結(jié)構(gòu)體變量中的成員各自占有自己的存儲空間,這是兩者的本質(zhì)區(qū)別。另外,共同體類型說明及變量定義都與結(jié)構(gòu)體類型說明及變量定義的方式類似。9.2共?同?體?類?型共同體類型說明的形式如下:

union共同體類型名

{成員列表;

};

其中union是C語言的關(guān)鍵字,共同體類型名只要符合C語言標(biāo)識符命名規(guī)則即可。

共同體變量的定義同結(jié)構(gòu)體變量的定義類似,也有三種方式,本節(jié)以第二種方式為例:

unionun1

{inta;

charb;

floatc;

}x;

由此看出,共同體“unionun1”共有三個成員,而其中成員c所需的存儲空間最大,是4個字節(jié)。所以共同體變量x共占用存儲空間4個字節(jié),它的三個成員共享此段空間。其存儲空間結(jié)構(gòu)如圖9.9所示。

說明:

在某一時刻,這段空間中只能存儲一個成員的數(shù)據(jù),這個數(shù)據(jù)就是最后一次賦予的值。

(2)不能對共同體變量進(jìn)行初始化,也不能進(jìn)行整體賦值運(yùn)算。

例如:

x.a=10;

x.b='e';

x.c=80.2;

則這段存儲空間保存的值是最后一次賦予的數(shù)據(jù)80.2。

圖9.9共同體存儲空間示意圖

例9.10

編一程序,實(shí)現(xiàn)輸出一個int型數(shù)據(jù)的高字節(jié)和低字節(jié)兩個數(shù)。

可以利用共同體的特點(diǎn)解決這個問題,程序如下:

#include"stdio.h"

uniondisa

{intx;

charch[2];

};

main()

{uniondisanum;

printf("pleaseenterainteger:");

scanf("%d",&num.x);

printf("low:%d,%c\n",num.ch[0],num.ch[0]);

printf("high:%d,%c\n",num.ch[1],num.ch[1]);

}

共同體變量num中包含兩個成員,兩個成員都是2個字節(jié),因此整個變量所占空間也是2個字節(jié)。

運(yùn)行程序時如果輸入16738,則系統(tǒng)會將整數(shù)16738以二進(jìn)制(0100000101100010)的形式存入存儲單元,如圖9.10所示。

從圖9.10中可以看出,低字節(jié)二進(jìn)制“01100010”轉(zhuǎn)換成十進(jìn)制為“98”,是字符“b”的ASCII值;而高字節(jié)二進(jìn)制“01000001”轉(zhuǎn)換成十進(jìn)制為“65”,是字符“A”的ASCII值。所以本程序的運(yùn)行結(jié)果如下:

low:98,b

high:65,A

圖9.10共同體變量

一般信息的存取都是以字節(jié)為單位的,但有時候因?yàn)閿?shù)據(jù)較小等原因,不需要一個字節(jié)的空間,C語言也為我們提供了可以按位存取的方法,這就是位段。

在定義結(jié)構(gòu)體時,以位為單位來聲明成員所占的內(nèi)存長度,這樣的成員就稱為位段或位域。例如:9.3位段結(jié)構(gòu)類型

structbitdata

{unsigneda:3;

unsignedb:5;

unsignedc:4;

unsignedd:4;

}i;

此結(jié)構(gòu)體中定義了4個位段a、b、c、d,分別占3位、5位、4位、4位,共2個字節(jié)。即變量i共占2個字節(jié),如圖9.11所示。

圖9.11位段結(jié)構(gòu)從圖9.11中可看出,位段a和b正好組合成一個字節(jié),而位段c和d也正好組合成一個字節(jié)。實(shí)際上,完全可以不必在意如此,C語言允許任意聲明位段1~8范圍內(nèi)的任意位數(shù)。例如:

structbitdata

{unsigneda:2;

unsignedb:5;

unsignedc:3;

unsignedd:2;

}x;現(xiàn)在大家要特別注意,位段a和b共7位,不滿一個字節(jié),還剩1位,但緊接其后的位段c卻需要3個字節(jié),所以系統(tǒng)會另外起一個存儲單元來存放位段c,同樣,第二個存儲單元也會出現(xiàn)3位空閑,如圖9.12所示。

圖9.12位段存儲示意圖此時變量x中的4個成員可以像前面章節(jié)中介紹的那樣進(jìn)行引用,也可以進(jìn)行相應(yīng)的數(shù)值運(yùn)算及輸入/輸出,但注意不要超出數(shù)值范圍,例如:

x.a=2; /*a位段只有2位,最大值為3,超出范圍則結(jié) 果出錯*/

x.b=x.a+10;

說明:

(1)位段成員的類型必須指定為unsigned或int類型。

(2)一個位段必須存儲在一個單元內(nèi),不能跨單元,如圖9.12中位段c所示。

(3)不能定義位段數(shù)組。

(4)可以使用如下形式,使某位段從另一字節(jié)開始存放:

structbitdata

{unsigneda:2;

unsignedb:3;

unsigned:0;

unsignedc:3;

};

雖然位段a、b、c可以存放在一個單元內(nèi),但我們使用“unsigned:0;”使位段c從下一個存儲單元開始存放。

枚舉類型是ANSIC新標(biāo)準(zhǔn)增加的類型。

“枚舉”就是一一列舉的意思,枚舉類型就是一一列舉出來變量的值,然后此變量就只能使用列舉出來的值。9.4枚舉類型

1.枚舉類型

枚舉類型的聲明如下例:

enumweek{sun,mon,tue,wed,thu,fri,sat};

其中,enum是C語言中的關(guān)鍵字?!皊un,mon,tue,wed,thu,fri,sat”這六個枚舉元素稱為枚舉常量,系統(tǒng)把它們當(dāng)作常量來使用。既然是常量,那就代表著一定的數(shù)值,系統(tǒng)按順序賦給它們的值分別是“0,1,2,3,4,5,6”。如果不希望得到系統(tǒng)默認(rèn)的值,也可以在聲明時指定數(shù)據(jù),例如:

enumweek{sun=7,mon=1,tue=2,wed=3,thu=4,fri=5,sat=6};

不管哪種聲明方式,枚舉常量都具有確定的值,因此也可以參與合理的數(shù)值運(yùn)算。例如:

mon-1

if(wed>sat)

2.枚舉變量

枚舉變量的定義同樣也有三種方式,例如:

enumweekworkday,weekday;

enumweek{sun,mon,tue,wed,thu,fri,sat}workday,weekday;

enum{sun,mon,tue,wed,thu,fri,sat}workday,weekday;

以上定義的枚舉變量workday,weekday的值只能是sun到sat其中之一,不能超出這個范圍,如下賦值語句是正確的:

workday=thu;

weekday=sun;

C語言允許用typedef來聲明一個新的類型名代替已有的類型名,這些已有的自定義類型名可以是整型、實(shí)型、字符型、結(jié)構(gòu)體型等。

聲明新類型名的形式如下:

typedef類型名新類型名;

其中,“類型名”是已經(jīng)聲明了的合法的類型,而“新類型名”只要是合法的C語言標(biāo)識符即可。9.5自?定?義?類?型例如:

typedefintINTEGER;

typedefcharCHARACTER;

注意,typedef語句并未產(chǎn)生新的類型,只是同一種類型又多了一個名稱,同時原有類型名也依然有效。如下兩個語句是等價的,都是定義了兩個整型變量a、b:

inta,b;

INTEGERa,b;

為了便于區(qū)別起見,新的類型名一般采用大寫形式。另外,利用typedef也可以為構(gòu)造類型聲明一個新名稱,例如:

typedefstruct

{intnum;

charname[20];

floatscore;

}STUDENT;這時候是對此結(jié)構(gòu)體類型聲明了一個名稱STUDENT,而絕不是定義了結(jié)構(gòu)體變量。下面我們可以用STUDENT來定義這種結(jié)構(gòu)體類型的變量或數(shù)組等:

STUDENTstu1,stu2,stu[30];

變量stu1、stu2都具有三個成員項(xiàng),數(shù)組stu每個元素也都具有三個成員。

9.6.1結(jié)構(gòu)體的應(yīng)用

例9.11

有一場比賽,其中參賽選手10名,編號為1~10,評委3位,選手的得分是三位評委打分的平均值,最后獲勝的只有一位最高分獲得者。請編寫函數(shù),利用結(jié)構(gòu)體數(shù)組計(jì)算每位選手的得分,并求出最高分,要求在主函數(shù)中輸入數(shù)據(jù)并輸出各選手得分和優(yōu)勝者的相關(guān)信息。9.6程序舉例

#include"stdio.h"

#defineN10

typedefstruct

{intsn;

floatscore1,score2,score3;

floataver;

}MATCH;

intpeak(MATCHplayer[])

{inti,j,m;

floatsum;

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

{sum=player[i].score1+player[i].score2+player[i].score3;

player[i].aver=sum/3;

}

m=player[0].aver;

j=0;

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

if(player[i].aver>m)

{m=player[i].aver;

j=i;

}

returnj;

}

main()

{MATCHpy[N];

inti,max;

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

py[i].sn=i+1;

printf("請輸入十個選手的得分:\n");

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

{printf("%d#:",py[i].sn);

scanf("%f,%f,%f",&py[i].score1,&py[i].score2,&py[i].score3);

}

max=peak(py);

printf("各選手的得分如下:\n");

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

printf("%8.2f",py[i].aver);

printf("優(yōu)勝者是:\n");

printf("%d號%6.2f",py[max].sn,py[max].aver);

}

實(shí)際生活中一般比賽分?jǐn)?shù)是這樣統(tǒng)計(jì)的:去掉最高分和最低分,然后再求平均值;而且一般還會將得分排序輸出。所以讀者可以自己思考,對本程序進(jìn)行修改完善。9.6.2*單鏈表的應(yīng)用

例9.12(其中劃線部分為要填空的空格。)設(shè)鏈表上每個結(jié)點(diǎn)的數(shù)據(jù)結(jié)構(gòu)為

typedefstructnode

{intd;

structnode*next;

}NODE;

函數(shù)NODE*invert(NODE*head)的功能是:將head指向的單鏈表逆置,即將原鏈表最后一個結(jié)點(diǎn)變?yōu)榈谝粋€結(jié)點(diǎn),原來倒數(shù)第二個結(jié)點(diǎn)變成第二個結(jié)點(diǎn),依此類推。在逆置過程中不建立新的鏈表。請?zhí)顚懲瓿珊瘮?shù)中的空格。算法提示:從前往后,逐個改變結(jié)點(diǎn)指針域的指向,如將第二個結(jié)點(diǎn)指向第一個結(jié)點(diǎn),將第三個結(jié)點(diǎn)指向第二個結(jié)點(diǎn),依此類推,直至尾結(jié)點(diǎn)。最后把原來第一個結(jié)點(diǎn)(逆置之后是最后一個)的next域置空,把原來最后一個結(jié)點(diǎn)(逆置之后是第一個)的地址賦予head。

NODE*invert(NODE*head)

{NODE*p,*q,*r;

if(head==0||head->next==0)

returnhead;

p=head;

q=p->next;

while(q!=0)

{r=[1];

q->next=p;p=q;q=r;

}

[2]=0;

head=p;

returnhead;

}

答案:[1]q->next[2]head->next

一、選擇題

1.已知有結(jié)構(gòu)類型定義:

typedefstructex{longintnum;

charsex;

structex*next;

}student;

下列敘述中錯誤的是()。

A.

structex是結(jié)構(gòu)類型B.

student是結(jié)構(gòu)類型的變量名

C.

ex可缺省 D.

student不可缺省習(xí)題9

2.已知有如下的結(jié)構(gòu)類型定義和變量聲明:

structstudent

{intnum;

charname[10];

}stu={1,"marry"},*p=&stu;

則下列語句中錯誤的是()。

A.

printf("%d",stu.num);B.

printf("%d",(&stu)->num);

C.

printf("%d",&stu->num);

D.

printf("%d",p->num);

3.已知結(jié)構(gòu)類型定義和變量聲明如下:

structsk

{inta;floatb;}data[2],*p;

若有p=data,則以下對data[0]中成員a的引用中錯誤的是()。

A.

data[0]->aB.

data->aC.

p->aD.

(*p).a

4.已有結(jié)構(gòu)類型定義和變量聲明如下:

structperson

{intnum;

charname[20],sex;

struct{intclass;charprof[20];}in;

}a={20,"lining",'M'{5,"computer"}},*p=&a;

下列語句中正確的是()。

A.

printf("%s",a->name);B.

printf("%s",p->f);

C.

printf("%s",*);

D.

printf("%c",p->in->prof);

二、閱讀程序題

1.以下程序的輸出結(jié)果為

。

#include<stdio.h>

structs

{inta;

structs*next;

};

main()

{inti;

staticstructsx[2]={5,&x[1],7,&x[0]},*ptr;

ptr=&x[0];

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

{printf("%d",ptr->a);ptr=ptr->next;}

}

2.以下程序在運(yùn)行時,輸出結(jié)果的第一行是

,第二行是

,第三行是

。

#include<stdio.h>

typedefstructs

{intindex;

intvalue;

}M;

main()

{staticinti,j,k,c[4][4];

Ma[10]={{0,1},{3,2},{5,3},{6,4},{9,5},{15,6},{-1,0}},*p=a,

b[10]={{1,1},{3,2},{4,3},{6,4},{10,5},{13,6},{-1,0}},*q=b;

while(p->index!=-1)

{i=p->index/4;

j=p->index%4;

c[i][j]=p->value;

p++;

}

while(q->index!=-1)

{i=q->index/4;

j=q->index%4;

c[i][j]=q->value;

q++;

}

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

{for(j=0;j<4;j++)

printf("%d",c[i][j]);

printf("\n");

}

}三、完善程序題

1.以下程序按結(jié)構(gòu)成員grade的值從大到小對結(jié)構(gòu)數(shù)組pu的全部元素進(jìn)行排序,并輸出經(jīng)過排序后的pu數(shù)組全部元素的值。排序算法為選擇法。

#include<stdio.h>

struct{

intid;

intgrade;

}STUD;

voidmain

{STUDpu[10]={{1,4},{2,9},{3,1},{4,5},{5,3},{6,2},{7,8},

{8,6},{9,5},{10,2}},*temp;

inti,j,k;

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

{k=

;

for(j=i+1;j<10;j++)

if(

)k=j;

if(k!=i)

{

溫馨提示

  • 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)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論