《清华大学C语言谭浩强第7章.ppt》由会员分享,可在线阅读,更多相关《清华大学C语言谭浩强第7章.ppt(127页珍藏版)》请在金锄头文库上搜索。
1、第第7章复合结构类型章复合结构类型第第7章章 复合结构类型复合结构类型7.1 结构体类型的概述 7.2 结构体类型的定义结构体类型的定义 7.3 结构体变量的定义及内存分配结构体变量的定义及内存分配 7.4 结构体变量的初始化和引用结构体变量的初始化和引用 7.5 结构体数组 7.6 结构体变量和结构体数组成员的访问结构体变量和结构体数组成员的访问 7.7 指向结构体类型数据的指针指向结构体类型数据的指针7.8 结构体型与函数结构体型与函数 7.9 用递归结构处理链表 7.10 共用体型共用体型 7.11 枚举类型枚举类型 7.12 用用typedef定义类型定义类型 7.1结构体类型的概述结
2、构体类型的概述所谓“结构体类型”是由不同数据类型变量组成的集合体,相当于其他高级语言中的记录。结构体类型的数据由若干称为“成员”的数据组成,每个成员既可以是一个基本数据类型的数据,也可以是另一个构造类型的数据。 例如:定义一个婴儿的出生证明的结构体。struct baby char name20; /* 婴儿姓名婴儿姓名 */ char sex; /* 性别性别 */ char date20; /* 出生日期出生日期 */ char num30; float weight; /* 出生时的体重出生时的体重 */ char place30; /* 出生地出生地 */;7.2结构体类型的定义结构体
3、类型的定义1. 结构体类型的定义说明结构体类型的定义说明2. 定义结构体类型时需要注意的问题定义结构体类型时需要注意的问题3. 结构体型的嵌套定义结构体型的嵌套定义1.结构体类型的定义说明结构体类型的定义说明结构体类型定义的一般格式为:struct 结构体名 数据类型1 成员名1; 数据类型2 成员名2; 数据类型3 成员名3; 数据类型n 成员名n;其中,struct是关键字, struct连同其后的结构体标识符一起称为结构体类型名或结构体名;结构体名是用户定义的标识符,其命名原则应符合标识符的书写规定;数据类型可以是基本类型说明符,也可以是已定义过的结构体类型名,还可以是后面将要介绍的共用
4、体型等;成员名也是用户自定义的标识符,用来标识所包含的成员名称;各成员的定义语句放在花括号中;花括号后面的分号是整个定义语句的结尾。例7.1 将学籍管理记录定义成一个名为struct student的结构体类型。struct student char number5,*name;/* 学号和姓名 */ char sex; /* 性别 */ int age; /* 年龄 */ float score; /*总分 */ char *speciality /*专业 */; char school31; /*学校 */; 2.定义结构体类型时需要注意的问题定义结构体类型时需要注意的问题(1)类型与变量
5、是不同的概念,不要混淆。)类型与变量是不同的概念,不要混淆。(2)每个成员都必须有自己的数据类型。)每个成员都必须有自己的数据类型。(3)结构体中的成员可以单独使用。)结构体中的成员可以单独使用。(4)结构体成员可以和程序中的其他标识符同名,也可)结构体成员可以和程序中的其他标识符同名,也可以和另一个结构体的成员同名。以和另一个结构体的成员同名。(5)结构体类型定义的位置,可以在函数内部,也可以)结构体类型定义的位置,可以在函数内部,也可以在函数外部。在函数外部。(6)结构体中的成员名可以与程序中的变量名相同,但)结构体中的成员名可以与程序中的变量名相同,但两者并不代表同一对象。两者并不代表同
6、一对象。(7)注意结构体和数组之间的区别:)注意结构体和数组之间的区别:数组里放的都是类型相同的数据(例如在一个箱子里放数组里放的都是类型相同的数据(例如在一个箱子里放的全是苹果)的全是苹果);而结构体中可以放不同类型的数据(例如在一个箱子里而结构体中可以放不同类型的数据(例如在一个箱子里既放有苹果,又放有桃、香蕉等不同的水果)。既放有苹果,又放有桃、香蕉等不同的水果)。3.结构体型的嵌套定义结构体型的嵌套定义结构体型的定义可以嵌套,即某个结构体型成员的数据类型可以说明为另一个已定义过的结构体类型。例如将上面定义的结构体类型中的成员“age”改为出生日期“birthday”。由于出生日期包含年
7、、月、日三个整型数据,可以将它定义成另外一个名为struct date的结构体类型的变量,并将该变量作为结构体类型struct student的一个成员,即struct date int year; int month; int day; ;/*struct student 结构体的定义如下*/struct student char num5,*name; char sex; struct date birthday; float score; char *speciality; char school31; ;也可以写成嵌套定义的形式:struct student char number5,
8、 *name; char sex; struct date int year; int month; int day; birthday; float score; char *speciality; char school31; ;7.3结构体变量的定义及内存分配结构体变量的定义及内存分配7.3.1 结构体变量的定义结构体变量的定义7.3.2 结构体类型的内存分配模式结构体类型的内存分配模式7.3.1结构体变量的定义结构体变量的定义1.先定义结构体,后定义变量先定义结构体,后定义变量2.在定义结构体类型的同时定义变量在定义结构体类型的同时定义变量3.直接说明结构体变量直接说明结构体变量1.先
9、定义结构体,后定义变量先定义结构体,后定义变量例如,为学生信息定义两个变量m和n,程序如下:struct student int num; char name20; int age; char sex; char place20;struct student m,n;/*定义结构体变量m和n*/上述程序就说明了两个struct student 型的变量m和n为。也可以在定义变量的同时,对变量赋初值,例如,上面的例子中定义的语句可以改写成如下赋初值的形式:struct student m=20041,“wangtai”,18,m, “江西江西, n=20043,zhaoli,16, f , 福建
10、福建;这种方法是将类型定义和变量定义分别进行的,是一种比较常用的定义方法。2.在定义结构体类型的同时定义变量在定义结构体类型的同时定义变量这种方法是在说明结构体变量的同时定义结构体变量。这种形式定义的一般格式为:struct 结构体名结构体名 成员列表成员列表变量名列表变量名列表;3.直接说明结构变量直接说明结构变量直接说明结构体类型变量的一般格式为:struct 成员表列成员表列变量名表列变量名表列;这种方法与第二种方法的区别在于第三种方法中省去了结构体名,而在后面直接给出结构体变量。例如:struct int num; char name20; int age; char sex; cha
11、r place20; m=20041,wangtai,18,m, 江西, n=20043,zhaoli,16,f, 福建;7.3.2结构体类型的内存分配模式结构体类型的内存分配模式结构体类型的内存分配模式随该类型中包含的成员不同而不同,需要的内存字节数等于各个成员所需要的内存字节数的总和。一旦定义了结构体类型的变量或数组后,就要按这种结构体类型的内存分配模式占用内存。 7.4.1 结构体变量的初始化结构体变量的初始化7.4.2 结构体变量的引用结构体变量的引用7.4结构体变量的初始化和引用结构体变量的初始化和引用7.4.1. 结构体变量的初始化结构体变量的初始化结构体变量的初始化形式与前面介绍
12、的数组相似,只要把对应各成员的初值放在花括号(即初值表)中即可。 例7.2 结构体变量的初始化。#includestruct student int number; char name20; char sex; struct date int year; int month; int day; birthday; float score; char speciality11; char school31; ; main() struct student st1=1001,Liming,0,1983,10,25,573, Computer,3th High School,Beijing; /*结
13、构体变量st1的定义和初始化*/ printf(%4d %-10s %c %4d %2d %2d %.2f %-10s %sn, st1.number,st1.name,st1.sex,st1.birthday.year, st1.birthday.month,st1.birthday.day,st1.score, st1.speciality,st1.school); /*输出结构体变量st1的各数据项*/程序运行结果为:程序运行结果为:1001 Liming 0 1983 10 25 573.00 Computer 3th High school,Beijing结构体变量初始化后,C编译系
14、统按结构体成员的顺序将各个初值置于各成员对应的存储单元,如表7-2所示。表表7-2 结构体构体变量初始化存量初始化存储示意示意numbernamesexyearmonthdayscorespecialityschool1001Liming01983102573Computer3th High School,Beijing注意:对结构体变量初始化时,不允许跳过前面的成员只给后面的成员赋初值,但可以只给前面的成员赋初值,后面未赋初值的成员自动赋零值(字符型数组赋空串)。7.4.2 结构体变量的引用结构体变量的引用不能直接使用结构体变量,只能通过引用其中的成员来实现对结构体变量的使用。引用结构体变量
15、成员的一般形式为:结构体变量名结构体变量名.成员名成员名例如: “m.num”表示m变量中的num成员,即m的num成员项。如果成员本身又是一个结构体,则必须逐级找到最低级的成员才能使用。例如: “m.birthday.month”表示m变量中的birthday成员中的month成员,即m变量出生的月份成员可以在程序中单独使用,与普通变量的使用完全相同。例7.3结构体变量成员的引用。#include string.hstruct student /* 定义student类型的变量 */ int num; char name20; char sex; float score;main() str
16、uct student m; m.num=20048; /* 给结构体变量x的成员num赋值 */ strcpy(m.name,zhangsan); /* 用strcpy给结构体变量x的成员name 赋值 */ m.sex=f; /* 给结构体变量x的成员sex赋值 */ m.score=95; /* 给结构体变量x的成员score赋值 */ printf(num=%d name=%s sex=%c score=%f, m.num, m.name, m.sex, m.score); /*输出结构体变量m的各数据项的值*/该程序的运行结果如下:num=20048 name=zhangsan se
17、x= f score=95.0000007.5结构体数组结构体数组7.5.1结构体数组的定义结构体数组的定义7.5.2结构体数组的初始化结构体数组的初始化7.5.1结构体数组的定义结构体数组的定义1.间接定义间接定义2.直接定义直接定义1.间接定义间接定义先定义结构体类型,再用结构体类型名定义该类型的数组,这是一种间接定义的方式。 例如:struct student int num; char name20; char sex; float score;struct student stu 3;2.直接定义直接定义在定义结构体类型的同时定义该类型的数组,这是一种直接定义的方式。例如:struc
18、t student char number5, *name; char sex; struct date int year; int month; int day; birthday; float score; char *speciality; char school31; st1,st210,*p1; 在直接定义时,也可以在struct后面不带结构体标识符,这种结构体类型称为无名结构体类型。例如:struct char number5, *name; char sex; struct date int year; int month; int day; birthday; float sc
19、ore; char *speciality; char school31; st1,st210,*p1;说明:有名结构体类型和无名结构体类型比较:(1)有名结构体类型的类型名可以使用多次,可以用它去定义其他的同类型变量、数组或指针。(2)无名结构体类型只能使用一次,不能用无名结构体类型再去定义其他的变量、数组或指针。 7.5.2结构体数组的初始化结构体数组的初始化结构体数组的初始化相当于给若干个结构体变量初始化,只要将各个元素的初值顺序放在内嵌的花括号中即可。当整个数组的各个元素都有初始化数据时,内嵌花括号也可以省略。例7.5以三个学生为例计算学生的平均成绩和及格人数。#includestru
20、ct student int num; char name20; char sex; float score;stu3= 11,Liang tao,t,80, 13,Zhao jun,f,97.5, 14,Hai kang,f,58; /*定义结构体数组并初始化各个数据项的内容*/main() int i,j=0; float average,sum=0; for(i=0;i=60) j+=1; /*统计及格人数*/ printf(the sum is %fn,sum); average=sum/3; /*求平均成绩*/ printf(the average is %fncount=%dn,
21、average,j);该程序的最后运行结果如下:该程序的最后运行结果如下:the sum is=235.500000the average is =78.500000count=27.6 结构体变量和结构体数组成员的访问结构体变量和结构体数组成员的访问7.6.1访问结构体成员运算符访问结构体成员运算符7.6.2 访问结构体成员的方法访问结构体成员的方法7.6.1访问结构体成员运算符访问结构体成员运算符1.“.”运算符运算符2.“-”运算符运算符1.“.”运算符运算符“.”运算符称点运算符。当用结构体变量名或结构体数组元素访问其成员时,要用“.”运算符。例如:“m.age”表示访问m的成员age
22、的值。2.“”运算符运算符“”运算符由一个减号和一个大于号组成。当用结构体指针或结构体数组名等地址量访问其成员时,要用“”运算符。例如:(n+1)age表示访问n1的成员age的值。说明:说明: “ “.”.”运算符和运算符和“-”-”运算符都是二元运运算符都是二元运算符,它们和算符,它们和“( )”( )”、“ ” ”一起处于最高优一起处于最高优先级,结合性均为从左至右。先级,结合性均为从左至右。7.6.2 访问结构体成员的方法访问结构体成员的方法1.访问结构体变量的成员访问结构体变量的成员2.访问结构体数组元素的成员访问结构体数组元素的成员1.访问结构体变量的成员访问结构体变量的成员若已定
23、义了一个结构体变量及指向它的指针,可以使用以下三种方法访问该结构体变量的成员:结构体变量名.成员名结构体指针名成员名(*结构体指针名).成员名2.访问结构体数组元素的成员访问结构体数组元素的成员若已定义了一个结构体数组及指向它的指针,则可以用数组名(相当于指针)、数组元素名(相当于变量)或指向该数组的指针,并通过相应的运算符“.”或“”来访问该数组元素的各个成员。主要有以下几种形式:结构体数组元素.成员名结构体数组名成员名结构体指针名成员名(*结构体指针名).成员名3.访问嵌套的结构体成员访问嵌套的结构体成员若结构体成员是内嵌的结构体时,必须用若干个“.”或“”来顺序地逐层定位到最里层的成员。
24、假设有一个结构体变量及指向它的结构体指针,该结构体变量的成员含有另一个结构体变量,则访问内嵌成员的形式有以下几种:结构体变量名.内嵌结构体变量名.成员名结构体指针名内嵌结构体变量名.成员名(*结构体指针名). 内嵌结构体变量名.成员名下面以结构体类型struct student为例,说明访问结构体成员的各种等价表示形式。若struct student/*定义结构体*/ char number5, *name; char sex; struct date int year; int month; int day; birthday; float score; char *speciality;
25、char school31; ;struct student st1,st210,*p1=&st1,*p2=st2;则访问结构体成员(包括变量、数组、指针和内嵌结构)的各种等价形式列于表7-3。表7-3 结构体成员访问形式示例访问对象结构体变量st1及其指针结构体数组元素st2j及其指针成员number第i个字符st1.numberip1numberi(*p1).numberist2j.numberi(st2+j)numberip2j.numberi(p2+j)numberi访问对象结构体变量st1及其指针结构体数组元素st2j及其指针成员number首地址st1.numberp1number
26、(*p1).numberst2j.number(st2+j)numberp2j.number(p2+j)number访问对象结构体变量st1及其指针结构体数组元素st2j及其指针成员numberi地址&st1.numberi&p1numberi&(*p1).numberi&st2j.numberi&(st2+j)numberi&p2j.numberi&(p2+j)numberi访问对象结构体变量st1及其指针结构体数组元素st2j及其指针成员name的第i个字符st1.nameip1namei(*p1).nameist2j.namei(st2+j)nameip2j.namei(p2+j)nam
27、ei访问对象结构体变量st1及其指针结构体数组元素st2j及其指针成员name的首地址st1.namep1name(*p1).namest2j.name(st2+j)namep2j.name(p2+j)name访问对象结构体变量st1及其指针结构体数组元素st2j及其指针成员score的值st1.scorep1score(*p1).scorest2j.score(st2+j)scorep2j.score(p2+j)score成员score首地址&st1.score&p1score&(*p1).score&st2j.score&(st2+j)score&p2j.score &(p2+j)scor
28、e访问对象结构体变量st1及其指针结构体数组元素st2j及其指针成员birthday首地址&st1.birthday&p1birthday&(*p1).birthday&st2j.birthday&(st2+j)birthday&p2j.birthday &(p2+j)birthday访问对象结构体变量st1及其指针结构体数组元素st2j及其指针二级成员year的值st1.birthday.yearp1birthday.year(*p1).birthday.yearst2j.birthday.year(st2+j)birthday.yearp2j.birthday.year(p2+j)birt
29、hday.year访问对象结构体变量st1及其指针结构体数组元素st2j及其指针二级成员year地址&st1.birthday.year&p1birthday.year&(*p1).birthday.year&st2j.birthday.year&p2j.birthday.year&(st2+j)birthday.year&(p2+j)birthday.year7.7指向结构体类型数据的指针指向结构体类型数据的指针7.7.1结构体指针的初始化结构体指针的初始化7.7.2结构体指针的应用结构体指针的应用7.7.1结构体指针的初始化结构体指针的初始化可以通过用结构体变量的首地址或结构体数组的数组名
30、作结构体类型指针的初值,从而使其指向对应的结构体变量或结构体数组。7.7.2结构体指针的应用结构体指针的应用1.指向结构体变量的指针指向结构体变量的指针2.指向结构体数组的指针指向结构体数组的指针1.指向结构体变量的指针指向结构体变量的指针一个结构体变量的指针就是该变量所占有的内存的起始地址。可以设一个指针变量,用来指向一个结构体变量。此时该指针变量的值是结构体变量的起始地址。例7.6指向结构体变量指针的应用。#includemain() struct student /*定义结构体*/ int num; char name20; char sex; float score; ; struct
31、 student stuone; /*定义结构体变量*/ struct student *p; /*定义结构体指针*/ p=&stuone; /*结构体指针指向结构体变量stuone*/ stuone.num=10871; strcpy(stuone.name,Mary); stuone.sex=F; stuone.score=98.0; printf(num:%dnname:%snsex:%cnscore:%.2fn, stuone.num, stuone.name,stuone.sex, stuone.score); /*直接输出结构体变量stuone中各个成员的数据值*/ printf(
32、num:%dnname:%snsex:%cnscore:%.2fn, (*p).num, (*p).name,(*p).sex,(*p).score); /*用指针的方式输出stuone中各个成员的数据值*/程序的输出结果为:num:10871name:Marysex:Fscore:98.00num:10871name:Marysex:Fscore:98.002.指向结构体数组的指针指向结构体数组的指针对结构体数组及其元素也可以使用指针来指向。例7.7指向结构体数组指针的应用。#includemain() struct baby int bornnum; char name20; char s
33、ex; char place20; ; struct baby bab2=201,Lile,M, Jiangshu, 112,Zhaoliang, F,Sandong; /*定义结构体数组并初始化*/ struct baby *p; /*定义结构体指针*/ for(p=bab;pbornnum,p-name, p-sex,p-place); 程序的运行结果为:程序的运行结果为: bornnum:201 name:Lile sex:M place:Jiangshu bornnum:112 name:Zhaoliang sex:F place:Sandong7.8 结构体型与函数 7.8.1 结构
34、体型的数据作函数的参数结构体型的数据作函数的参数7.8.2 结构体型的函数结构体型的函数7.8.1结构体型的数据作函数的参数结构体型的数据作函数的参数1. 结构体变量的成员作参数结构体变量的成员作参数2. 结构体变量作参数结构体变量作参数3. 指向结构体变量(或数组)的指针作参数指向结构体变量(或数组)的指针作参数1.结构体变量的成员作参数结构体变量的成员作参数调用函数将单个结构体成员传递给被调用函数的用法与普通变量做参数一样主要有以下三种形式:(1)如果该结构体成员是简单变量,则被调用函数对应的形参应该是同类型的简单变量。(2)如果该结构体成员是变量的地址,则被调用函数中对应的形参应该是指针
35、变量。(3)如果该结构体成员是数组或指向数组的指针,则被调用函数对应的形参应该是数组名或指针。例例7.8假设学生的三门课成绩已保存在一个结构数组中,假设学生的三门课成绩已保存在一个结构数组中,要求用子程序计算并返回三门课的总分。要求用子程序计算并返回三门课的总分。在主程序在主程序main()main()中先中先定义一个结构体类型,再创建一个结构体数组,并将该数组的定义一个结构体类型,再创建一个结构体数组,并将该数组的成员成员scorescore(数组)传递到子程序(数组)传递到子程序add()add()中,由中,由add()add()计算总分计算总分并返回。并返回。#include main(
36、) struct stu; /*定义结构体*/ char name80; int score3; int total; ; struct stu student3; /*定义结构体数组*/ int i; printf(Please enter three stuname and three score for the three stu:n); for(i=0;i3;i+) /*将学生的课程成绩输入结构体数组中*/ scanf(%s,studenti.name); scanf(%d%d%d,&studenti.score0, &studenti.score1,&studenti.score2)
37、; studenti.total=add( studenti.score0, studenti.score1, studenti.score2); /*调用函数add计算学生课程成绩的总分*/ printf(The score_ table of the three students is:n); for(i=0;i3;i+) printf(%10s %d %d %d %dn,studenti.name, studenti.score0, studenti.score1, studenti.score2, studenti.total); /*输出结构体数组中各个数据项的值*/add(int
38、x,int y,int z) /*定义计算总分的函数add*/ return x+y+z;程序的运行情况为:Please enter three stuname and three score for the three stu:zhangli989097wanghong898898liufei788997The score_ table of the three students is : zhangli 989097285 wanghong898898275 liufei 788997264在上述程序中要传递的成员是一个数组,因此需要将该数组的各个元素作为单个变量来传递。2.结构体变量作参
39、数结构体变量作参数调用函数将结构体变量整体传递给被调用函数时,采用的是“值传递”的方式。此时将结构体变量所占内存单元的内容全部顺序传递给形参(形参必须是同类型的结构体变量)。具体可以采用以下三种形式:(1)实参和形参都是结构体变量名。(2)实参是结构体变量的地址,形参是相同结构体类型的指针。(3)实参和形参都是相同结构体类型的指针。 例7.9用结构体变量的地址作为实参调用函数的应用举例。 #include struct score /*定义课程分数的结构体*/ int math; int chinese; int english;struct information /*定义学生信息的结构体*
40、/ int num; char name20; struct score sco; int average_score;void getinfo(struct information *p); /* getinfo函数的说明*/void comp(struct information *p); /* comp函数的说明*/main() struct information exam; /*定义结构体变量exam*/ getinfo(&exam); /*调用getinfo函数*/ comp(&exam); /* 调用comp函数*/void getinfo(struct information
41、*p) /*函数getinfo 的定义*/ printf(Please enter the information of one student:n); printf(No:); scanf(%d,&p-num); /*输入学生的学号*/ printf(Name:); scanf(%s,p-name); /*输入学生的名字*/ printf(The three score(math,chinese,english) of %s is:n,p-name); scanf(%d,%d,%d,&(p-sco.math), &(p-sco.chinese), &(p-sco.english); /*输入
42、学生的各科分数*/ void comp(struct information *p) p-average_score=(p-sco.math+ p-sco.chinese+p-sco.english)/3; /*求平均成绩*/ printf(The information about %s is:n, p-name); printf(No:%dnName:%snmath:%3d,chinese: %3d,english:%3d, average_score:%3dn, p-num,p-name,p-sco.math, p-sco.chinese,p-sco.english, p-average
43、_score); if(p-average_score)=85) /*根据平均成绩判断该学生是否优秀*/ printf(Excellent!n); else printf(General!n);程序的运行情况为:程序的运行情况为:Please enter the information of one student:No:12 Name:Jim The three score(math,chinese,english) of Jim is:98,90,97 The information about Jim is:No:12Name:Jimmath:98,hinese:90,English:9
44、7,average_score:95Excellent!3.指向结构体变量(或数组)的指针作参数指向结构体变量(或数组)的指针作参数 用指向结构体变量(或数组)的指针作为实参,将结构体变量(数组)的地址传给形参。可以提高程序的运行效率。例7.10有一个结构体变量有一个结构体变量stu,内含学生学号、姓名和,内含学生学号、姓名和3门课的成绩。要求在门课的成绩。要求在main函数中赋以值,在另一函数函数中赋以值,在另一函数pirnt中将它们打印输出。中将它们打印输出。#includestruct student int num; char name20;int score3; stu=10020,
45、Mary,89,98,79;/*定义结构体变量stu并进行初始化*/void print(struct student *p) /*定义print函数*/ printf(%dn%sn%dn%dn%dn,p-num, p-name, p-score0, p-score1, p-score2); /*输出指针p所指学生的输出成绩*/ printf(n);main() print(&stu); /*调用函数print*/程序的输出结果为:程序的输出结果为:10020Mary8998797.8.2结构体型的函数结构体型的函数1.结构体型函数结构体型函数2.结构体指针型函数结构体指针型函数1.结构体型函
46、数结构体型函数结构体型函数定义的一般形式为:struct 结构体名结构体名 函数名函数名(形参表形参表) 函数体函数体例如:struct student function(int x,int y) /*函数头*/ 函数体上述程序定义了一个上述程序定义了一个struct student结构体类型的函结构体类型的函数,它可以将一个结构体变量返回到调用函数。数,它可以将一个结构体变量返回到调用函数。 例7.11一个通讯录中包括联系人的编号、姓名、电话、电子邮件、居住地。根据编号查找并输出查找到的联系人的信息。#include struct person /*定义结构体数组,并将联系人的相关信息存入该
47、数组*/ int num; char name20; char telephone12; char email40; char addr30;con_person3=9,Zhangli,13817890789, ,Shanghai,6,Jack,01089789091,, Bejing,12,Qiuzhi,13762457986,,Hangzhou;struct person find(int n) /*定义查找函数find*/ int i; for (i=0;con_personi.num!=0;i+) if (con_personi.num=n) return(con_personi);m
48、ain() int number; struct person result; /*定义结构体变量*/ printf(Enter the number:); scanf(%d,&number); /*输入编号*/ result=find(number); /*调用find函数*/ if(result.num!=0) /*找到联系人后输出其信息*/ printf(Name:%sn,result.name); printf(Telephpne:%sn,result.telephone); printf(Email:%sn,result.email); printf(Address:%sn,resu
49、lt.addr); else printf(Not found!n);程序的运行情况为:Enter the number:12Name:QiuzhiTelephone:13762457986Email:Address:HangzhouEnter the number:9Name:ZhangliTelephone:13817890789Email: Address:ShanghaiEnter the number:1Not found!2.结构体指针型函数结构体指针型函数结构体指针型函数定义的一般形式为:struct 结构名结构名 *函数名函数名(形参表形参表) 函数体函数体例如:struct
50、student *function(int x,int y) /*函数头函数头*/ 函数体函数体 例7.12将例7.11程序中的结构体型函数改成结构体型指针型函数。#include struct person /*定义结构体数组并进行初始化赋值*/ int num; char name20; char telephone12; char email40; char addr30;con_person3=9,Zhangli,13817890789, ,Shanghai, 6,Jack,01089789091, , Bejing, 12,Qiuzhi,13762457986, ,Hangzhou;
51、struct person *find(int n) /*定义函数find*/ int i; for(i=0;con_personi.num!=0;i+) if(con_personi.num=n) return(&con_personi);main() int number; struct person *result; printf(Enter the number:); scanf(%d,&number); result=find(number); /*调用函数find*/ if(result-num!=0) /*找到联系人后输出其信息*/ printf(Name:%sn,result-
52、name); printf(Telephpne:%sn,result-telephone); printf(Email:%sn,result-email); printf(Address:%sn,result-addr); else printf(Not found!n);程序的运行情况为:Enter the number:6Name:JackTelephone:01089789091Email: Address:BeijingEnter the number:9Name:ZhangliTelephone:13817890789Email: Address:ShanghaiEnter the
53、number:10Not found!7.9 用递归结构处理链表用递归结构处理链表7.9.1递归结构递归结构7.9.2链表的概念链表的概念7.9.3动态链表的基本操作动态链表的基本操作7.9.1递归结构递归结构如果一个结构体类型的某些成员是该结构体类型的变量、数组或指针,则称之为递归结构,也叫自嵌套结构。使用最多的递归结构是:结构体的一个成员只能是指向本结构体类型的指针。例如:struct node int data; struct node *next;这种递归结构在处理诸如链表这样的数据结构时特别这种递归结构在处理诸如链表这样的数据结构时特别有用。有用。7.9.2链表的概念链表的概念链表是
54、一种动态的数据结构。单向链表由若干个节点首尾相接而成,每个节点有两个域:数据域data存放数据,指针域next存放下一节点的首地址,如图7-2所示。 图图7-2 单向链表单向链表单向链表中的节点可用如下递归结构来描述:struct node int data; struct node *next;通过指针将各个节点链接起来,就构成单向链表。例如,下面的定义和语句可以描述一个含有三个节点的单向链表:struct node int data; struct node *next;a,b,c,*p=&a; a.next=&b; b.next=&c; c.next=NULL ;7.9.3动态链表的基本
55、操作动态链表的基本操作1.建立链表建立链表2.输出链表输出链表1.建立链表建立链表建立链表的过程是:先建立链表的头节点,并将该头节点作为尾节点,然后不断增加新节点,将新增的节点连接在当前尾节点的后面而作为新的尾节点。需要设置三个指针,h指向链表的头节点,p指向新建节点,q指向尾节点,如图7-3所示。qdata nextdata next hp(新建节点)(新建节点)图图7-3 链表工作指针示意图链表工作指针示意图2.输出链表输出链表输出链表的过程是:根据链表的头节点找到下一个节点;先输出其data域中的数据;然后根据其next域中的地址,取出后继节点,输出其data域中的数据。如此不断选取下一
56、个节点,直到链表末尾。例7.13先建立一个单向链表,将键盘输入的整数1、2、3、4、5、6、7、8、9、10依次存入该链表各个节点的数据域中,当输入整数0时,结束建立链表的操作。然后依次输出链表中的数据,直到链表末尾。 #include struct node /*递归定义单链表的节点*/ int data; struct node *next; ;struct node *creatlist() /*定义创建单链表的函数*/ struct node *h,*p,*q; int a; h=(struct node *)malloc(sizeof(struct node); /*头节点,即链表起
57、始地址*/ p=q=h; scanf(%d,&a); while (a!=0) /*插入节点的起始地址*/ p=(struct node *)malloc(sizeof(struct node); p-data=a; q-next=p;/*将插入节点的起始地址存入当前节点的next域*/ q=p; /*将当前节点作为新的尾节点*/ scanf(%d,&a); p-next=NULL; /*链表尾节点*/ return h;void printlist(struct node *h) struct node *p; p=h-next; /*取头节点的后继节点*/ while (p!=NULL)
58、printf(-%d ,p-data); p=p-next; /*取下一个后继节点*/ printf(n); return;main() struct node *head; head=creatlist(); /*创建链表并保存链表起始地址*/ printlist(head); /*输出链表*/程序运行结果为:1 2 3 4 5 6 7 8 9 10 0 (键盘输入)-1 -2 -3 -4 -5 -6 -7 -8 -9 -10 (输出结果)7.10共用体型共用体型共用体由若干个数据类型组合而成。和结构体型不同的是,共用体数据中所有成员都占用相同的内存单元,设置这种数据类型的主要目的就是节省内
59、存空间。例7.14在一个函数的三个不同的程序段中分别使用了字符型变量c1、整型变量d1、单精度型变量f1,这时就可以把它们定义成一个共用体变量un,un中含有三个不同类型的成员。为了节省空间,假如现在只给三个成员一共分配4个内存单元,那么3个成员之间的对应关系如下图7-4所示。共用体共用体变量变量un 图图7-4 共用体内存分配示意图共用体内存分配示意图 7.10.1共用体的定义共用体的定义共用体定义的一般形式为: union 共用体名共用体名 数据类型数据类型1 成员名成员名1; 数据类型数据类型2 成员名成员名2; 数据类型数据类型n 成员名成员名n; 共用体名是用户自定义的标识符;数据类
60、型通常是前面所使用的基本数据类型,也可以是结构体型、共用体型等构造类型。 例如,以下代码说明了一个共用体un1: union un1 int a100; char m100; float n200;其中,成员a占用了200个单元的内存空间,因为a为整型,而一个整型占2个单元的空间,所以2100=200。同理,成员m占用了1100=100个内存单元;成员n占用了4200=800个内存单元,所以,该共用体共占用了800个单元空间。 7.10.2共用体变量的定义共用体变量的定义共用体变量定义的方法也有三种:(1)先说明共用体,再定义共用体变量。(2)在说明共用体类型的同时定义共用体变量。(3)直接定
61、义共用体变量。例如,将含有10个元素的整型数组m10、字符型数组n35、双精度数据a定义成一个共用体un,然后再定义这个共用体的变量x和y。下面分别用上述的三种方法进行定义。(3)定义无名称的共用体,同时定义变量x和y。union int m10; char n35; double a;x,y;(1)先定义共用型,然后再定义变量x和y。union un int m10; /* 该成员共占用了20个单元空间 */ char n35; /* 该成员共占用了15个单元空间 */ double a; /* 该成员共占用了8个单元空间 */; /* 该共用体共占用了20个单元空间 */union un
62、x,y; /* 定义两个共用体变量x和y */(2)同时定义共用体un和变量x、y。union un int m10; char n35; double a;x,y;7.10.3共用体变量的引用共用体变量的引用1.直接引用直接引用2.通过指向共用体变量的指针进行引用通过指向共用体变量的指针进行引用3.共用体变量与函数共用体变量与函数1.直接引用直接引用共用体变量成员的直接引用是通过运算符“.”实现的。其引用的一般格式为: 共用体变量名共用体变量名. .成员名成员名例如:union int m; char ch; double a;x;printf(%d,%c,%lf,x.m,x.ch,x.a)
63、;上述printf语句中的x.m、x.ch、x.a是对共用体变量x成员m、ch、a的引用。2.通过指向共用体变量的指针进行引用通过指向共用体变量的指针进行引用通过指向共用体变量的指针可以对共用体变量的成员进行存取操作。其一般的语法格式为: 指向结构体变量的指针成员名或(*指向结构体变量的指针).成员名 例7.15通过共用体变量的指针对共用体变量中的成员进行引用的应用举例。#includeunion stu /*定义共用体*/ char *name; int num;main() union stu *p,stuone; /*定义共用体指针和共用体变量*/ p=&stuone; /*指针p指向共
64、用体变量stuone*/ stuone.num=101; /*为stuone的成员num赋值*/ printf(%dn,pnum); /*用指针输出num的值*/程序段的输出结果为:程序段的输出结果为:101说明:指向共用体变量的指针对共用体变量的操作可以通说明:指向共用体变量的指针对共用体变量的操作可以通过强制转换后的指针存取共用体变量的成员,而不必指定成过强制转换后的指针存取共用体变量的成员,而不必指定成员名。系统会根据强制转换的数据类型给与该类型相同的数员名。系统会根据强制转换的数据类型给与该类型相同的数据成员赋值。据成员赋值。例7.16通过强制转换后的指针引用共用体变量成员的举例。#i
65、ncludeunion worker /*定义共用体*/ float salary; int number;main() union worker *p,personone;/*定义共用体指针和共用体变量*/ p=&personone;/*指针p指向共用体变量personone*/ *(float*)p=4567.0; /*为personone的成员salary赋值*/ printf(“n%.2fn,personone.salary); *(int *)p=112; /*为变量personone的成员number赋值*/ printf(%d,personone.number);程序的运行结果为
66、:程序的运行结果为:4567.001123.共用体变量与函数共用体变量与函数共用体变量本身不能作为函数的参数,但共用体成员可以在函数中进行引用。而且函数也不能返回共用体变量。但是指向共用体变量的指针可以作为函数的参数,返回值也可以是指向共用体变量的指针。例7.17共用体变量的成员和共用体指针作函数参数的应用举例。#includeunion traffic char *name; int num;void road(char *x) /*定义road函数*/printf(The name of traffic is %s!n,x);void air(union traffic *x) /*定义a
67、ir函数*/printf(The name of traffic is %s!n,x-name);main()union traffic *p,traone;/*定义共用体指针和共用体变量*/p=&traone; /*指针p指向共用体变量traone*/traone.name=Car; /*为变量traone的成员name赋值*/road(traone.name); /*调用road函数*/air(p); /*调用air函数*/程序的运行结果为:程序的运行结果为:The name of traffic is car!The name of traffic is car!7.10.4共用体类型数
68、据的特点共用体类型数据的特点(1)共用体中的多个成员共用一个内存段,但同一瞬间只有一个成员占用内存段,即每一瞬时只有一个成员存在和起作用,不能所有的成员同时在内存段中起作用。(2)共用体变量的地址和共用体各个成员的地址都是同一个地址。例如:unoin sr char ch1; float a;union sr str; /*定义共用体变量str*/此时,&str、&str.ch1、&str.a的地址值是同一个。(3)共用体中起作用的成员是最后一次存放的成员。 例如:unoin cc int a; int b; int c;unoin cc ch1; /*定义共用体变量ch1*/ch1.a=2;
69、 /*为ch1的成员a赋值*/ch1.b=3; /*为ch1的成员b赋值*/ch1.c=4; /*为ch1的成员c赋值*/此时若执行以下语句:printf(%d,ch1.a);printf(%d,ch1.b);printf(%d,ch1.c);则三条语句的输出结果均为4。(4)共用体类型可以在结构)共用体类型可以在结构体类型中定义,结构体类型体类型中定义,结构体类型也可以在共用体类型中定义。也可以在共用体类型中定义。例如:以下共用体例如:以下共用体cc的成员的成员中就有结构体类型的变量中就有结构体类型的变量bb。unoin cc /*定义共用体定义共用体*/ int a; char b20;
70、struct dd int i; int j; bb; /*定义结构体变量定义结构体变量bb*/; (5)共用体类型可以嵌套定义。例如: unoin aa int a; char b20; unoin bb int i; int j; c; /*定义共用体变量c*/;(6)不能对共用体变量名赋值,也不能通过引用变量名得到一个值。 (7)共用体类型的变量不能作为函数的参数,也不能使函数返回共用体变量。但可以使用共用体变量的指针。(8)不能在定义共用体变量时对其进行初始化。例如下面的初始化是错误的:unoin cc int a; int b; int c;s=2,3,4;7.10.5结构体和共用体
71、的区别结构体和共用体的区别结构体和共用体之间存在着以下的区别:(1)结构体和共用体都是由多个不同的数据类型成员组成, 但在同一时刻, 共用体中只存放一个被选中的成员, 而结构体的所有成员都存在。 (2)对共用体的不同成员赋值, 将会造成对其他成员的重写, 原来成员的值就不存在了, 而对于结构体的不同成员赋值是互不影响的。例7.18共用体和结构体应用举例。#includemain() union /* 定义一个共用体 */ int i; struct /* 在共用体中定义一个结构体 */ char first; char second; sql_1; /* 定义一个结构体变量sql_1 */nu
72、mber; /* 定义一个共用体变量number */number.i=0x4445; /* 给共用体变量的成员赋值 */ printf(%c%c%xn, number.sql_1.first, number.sql_1.second,number.i); number.sql_1.first=c; /* 给共用体变量中的结构体成员赋值 */ number.sql_1.second=d; printf(%xn, number.i); 程序的输出结果为:ED44456463说明:从上面的结果可以看出:当给说明:从上面的结果可以看出:当给i i赋值后赋值后, , 其低八其低八位是位是firstfi
73、rst的值,而高八位是的值,而高八位是secondsecond的值,当给的值,当给firstfirst和和secondsecond赋字符后赋字符后, i, i的值也跟随发生了变化。的值也跟随发生了变化。7.11枚举类型枚举类型7.11.1枚举型的定义枚举型的定义7.11.2枚举变量的定义枚举变量的定义7.11.3给枚举常量赋初值给枚举常量赋初值7.11.4枚举中的几条规定枚举中的几条规定7.11.1枚举型的定义枚举型的定义枚举型定义的一般形式为:enum 枚举名 枚举常量1,枚举常量2,枚举常量n ;其中的枚举常量是用户给常量取的标识符,有固定的值,在一个含有n个枚举常量的枚举型中,C语言规定
74、枚举常量的值按枚举的顺序分别依次等于0、1、 n-1。例如,定义一个表示星期的枚举型。enum week sun,mon,tue,wed,thu,fri,sat ;上述语句定义了一个名为上述语句定义了一个名为“weekweek”的枚举类型,该枚举的枚举类型,该枚举型中有型中有7 7个枚举常量,分别是:个枚举常量,分别是:sunsun、monmon、tuetue、wedwed、thuthu、frifri和和satsat,即一周中的七天。每个枚举常量按规,即一周中的七天。每个枚举常量按规定其值从定其值从0 0开始依次为开始依次为0 0、1 1、2 2、3 3、4 4、5 5、6 6。 7.11.2
75、枚举变量的定义枚举变量的定义枚举变量的定义和结构体相似,有以下三种方式:(1)先定义枚举型,再定义枚举变量。 enum 枚举名枚举名 变量名表变量名表; 例如:enum traffic train,plane,car,ship ;enum traffic trone,trtwo;上述语句定义了两个枚举变量:trone和trtwo。(2)在说明枚举型的同时定义枚举型的变量。 例如:enum traffictrain,plane,car,shiptrone,trtwo;(3)不说明枚举型,直接定义枚举变量。例如:enum train,plane,car,shiptrone,trtwo;7.11.3
76、给枚举常量赋初值给枚举常量赋初值枚举常量赋初值的方法是在枚举常量的后面跟上“=整型常量”。例如,下面是一个表示三原色的枚举型:enum colorred=3,yellow=5,blue=8;此时,枚举常量red的值为3,yellow的值为5,blue的值为8。在给枚举常量赋初值时,如果给其中任何一个枚举常量赋初值,则后面的枚举常量将按自然数的规则依次赋初值,例如:enum weekday sun,mon,tue,wed=6,thu,fri,sat ;上面的例子只给枚举常量wed赋了初值,在这种情况下,每个枚举常量的初值分别如下:sun的值为0,mon的值为1,tue的值为2,wed的值为6,t
77、hu的值为7,fri值为8,sat值为9。7.11.4枚举中的几条规定枚举中的几条规定(1)枚举变量的取值只能在枚举常量的范围内,不能取其他任何数。例如,如果变量x、y、z被说明为枚举型week的变量enum week sun,mon,tue,wed,thu,fri,sat ;enum week x,y,z;则x=sun; y=tue; z=fri; 都是正确的。 (2)枚举值是常量,不是变量。不能企图在程序中用赋值语句再对枚举值进行赋值。也就是不能将其当作变量使用(即不能在赋值号的左边使用枚举常量)。例如,对枚举week的元素继续作以下赋值:mon =7; thu=3; sat=tue; 则
78、都是错误的。(3)枚举元素本身由系统定义了一个表示序号的数值,从0开始顺序定义为0、1、2。例7.19枚举型week中枚举常量的数值。#includemain() enum week sun,mon,tue,wed,thu,fri,sat x,y,z; /*定义枚举变量x、y、z*/ x=sun; /*为x赋值*/ y=mon; /*为y赋值*/ z=thu; /*为z赋值*/ printf(sun is :%dnmon is :%dnthu is :%dn,x,y,z); 程序的运行结果如下:程序的运行结果如下:sun is :0mon is :1thu is :4(4)可以把枚举值赋予枚举
79、变量,但是,却不能把元素的数值直接赋予枚举变量。例如,x=sum; y=mon; 表示是正确的。而x=0;y=4;表示是错误的。(5)如果一定要把数值赋予枚举变量,则必须用强制类型转换。即用强制类型转换可以将数值赋予枚举变量。例如:enum week sun,mon,tue,wed,thu,fri,sat ;enum week x;x=(enum week)5;上述赋值表达式的意思是将顺序号为5的枚举元素赋予枚举变量x,其相当于:x=fri; (6)枚举元素不是字符型常量也不是字符串常量,使用时不要加单引号或双引号。(7)可以用比较运算符对两个枚举变量或数组元素进行“大小”的比较,比较时可以按
80、照变量或数组元素的枚举型常量值的大小进行。例7.20枚举型数组元素比较大小。#includemain() enum weeksun,mon,tue,wed,thu,fri,satwk7; /*定义枚举型的数组wk*/ int i,k1,k2; for(i=0;i7;i+) wki=(enum week)i; /*为数组wk的元素赋值*/ k1=wk0wk2; /*比较数组wk的0号元素和2号元素数值的大小*/ k2=wk3wk1; /*比较数组wk的1号元素和3号元素数值的大小*/ printf(k1=%dnk2=%dn,k1,k2);程序的输出结果为:程序的输出结果为:K1=1K2=07.1
81、2用用typedef定义类型定义类型7.12.1 typedef定义类型的格式7.12.2 typedef定义类型的几点说明7.12.1 typedef定义类型的格式定义类型的格式typedef定义的一般格式为:typedef 原类型名原类型名 新类型名新类型名;例如:typedef int INTEGER;/*指定INTEGER代表int类型*/typedef float REAL; /*指定REAL代表float类型*/用上述两条语句定义完后,则int a;和 INTEGER a;是等价的。 7.12.2 typedef定义类型的几点说明定义类型的几点说明(1)用typedef只是为已经存在的类型声明了一个新名字,并没有创造新的类型,也不能取代现有的类型名。(2)用typedef可以将已有的各种类型名定义成新的类型名,但不能直接定义变量。(3)typedef与#define有相似之处,例如:typedef int INTEGER与#define INTEGER int都是用INTEGER代表int 。但#define是在预编译时处理的,只能作简单的字符串替换;而typedef是在编译时处理的,并不是作简单的字符串替换。