版權(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)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年度綠色金融創(chuàng)新產(chǎn)品開發(fā)貸款合同3篇
- 2024質(zhì)保協(xié)議書范本
- 2024葡萄品種專項(xiàng)銷售代理協(xié)議版B版
- 2024跨區(qū)域連鎖加盟門店承包合同
- 2024版最正式的借款合同
- 二零二五年度電商綠色物流合作協(xié)議3篇
- 2024軟件許可合同 with 軟件功能與技術(shù)支持服務(wù)
- 二零二五年度陜西省旅游項(xiàng)目開發(fā)合作合同2篇
- 西安文理學(xué)院《汽車試驗(yàn)技術(shù)及性能試驗(yàn)》2023-2024學(xué)年第一學(xué)期期末試卷
- 2025年度國際貿(mào)易供應(yīng)鏈合同解析3篇
- 2024年公務(wù)員考試《公共基礎(chǔ)知識》全真模擬試題1000題及答案
- DB3301T 0382-2022 公共資源交易開評標(biāo)數(shù)字見證服務(wù)規(guī)范
- 幼兒教育專業(yè)國家技能人才培養(yǎng)工學(xué)一體化課程設(shè)置方案
- 2025年會計(jì)從業(yè)資格考試電算化考試題庫及答案(共480題)
- 江蘇省無錫市2023-2024學(xué)年八年級上學(xué)期期末數(shù)學(xué)試題(原卷版)
- DL-T 5876-2024 水工瀝青混凝土應(yīng)用酸性骨料技術(shù)規(guī)范
- GB/T 44889-2024機(jī)關(guān)運(yùn)行成本統(tǒng)計(jì)指南
- 2024年6月英語六級考試真題及答案(第2套)
- 職業(yè)院校技能大賽(高職組)市政管線(道)數(shù)字化施工賽項(xiàng)考試題庫(含答案)
- 危險化學(xué)品目錄(2024版)
- 華為經(jīng)營管理-華為的股權(quán)激勵(6版)
評論
0/150
提交評論