《程序设计基础》课件第9章_第1页
《程序设计基础》课件第9章_第2页
《程序设计基础》课件第9章_第3页
《程序设计基础》课件第9章_第4页
《程序设计基础》课件第9章_第5页
已阅读5页,还剩84页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第9章结构体、共用体和枚举类型9.1结构体

9.2单链表

9.3共用体和枚举类型

9.4typedef定义类型

习题 9.1结构体

在实际应用中,一组数据往往具有不同的数据类型。例如在学生登记表中,学号、姓名、性别、年龄、电话号码和家庭地址的数据类型不完全相同。显然不能用数组来存放这一组数据,因为数组中各元素的类型和长度都必须一致。为了解决这个问题,C语言中给出了另一种构造数据类型——结构体。结构体是一种构造类型,它是由若干“成员”组成的。每一个成员可以是一个基本数据类型,或者是另一个构造类型。结构体既然是一种“构造”而成的数据类型,那么在说明和使用之前必须先定义结构体类型。9.1.1结构体的类型定义

结构体是由程序设计者自己定义的类型。因此,除了结构体变量需要定义之外,结构体类型本身也必须定义。定义结构体类型的一般形式如下:

struct结构体名

{

数据类型1成员1;

数据类型2成员2;

数据类型n成员n;

};说明:

(1) struct是定义结构体类型的关键字,不能省略。

(2)结构体名遵循标识符的命名规则。

(3)结构体有若干数据成员,用{}括起来,分别属于各自的数据类型。结构体成员名同样遵循标识符的命名规则。

(4)定义结构体类型,就是定义一种数据类型,与基本数据类型是一样的,只不过结构体类型是一种复杂的数据类型,是基本数据类型的组合。应注意,使用结构体类型时,“struct结构体名”是作为一个类型名来对待的,它与标准类型(如int、char等)具有相同的地位和作用。

(5)定义结构体类型后,C系统并不分配存储空间,只有定义了该结构体类型的变量后,系统才为此变量分配存储空间。

例9-1

定义一个学生信息的结构体类型。structstudent

{

intnum;

charname[19];

charsex;

intage;

chartel[20];

charaddr[40];

};说明:

(1)“structstudent”是结构体类型名。struct是关键字,在定义和使用时均不能省略。

(2)该结构体类型由6个成员组成。这6个成员分别属于不同的数据类型,各个成员之后的分号“;”不能省略。应特别注意的是,最后的分号是必不可少的。9.1.2结构体数据的定义和引用

1.先声明结构体类型再定义变量名

如上面已经定义了一个结构体类型structstudent,可以用它来定义变量。如:

structstudentstu1,stu2;

定义了两个变量stu1和stu2,为structstudent类型的变量。在定义了结构体变量之后,系统会为之分配内存单元。例如stu1和stu2在内存中所占的字节是structstudent类型中各个成员所占字节数之和,即4+19+1+4+20+40=88字节。2.在声明结构体类型的同时定义变量

例如:

structstudent

{

intnum;

charname[19];

charsex;

intage;

chartel[20];

charaddr[40];

}stu1,stu2;它的作用与第一种方法相同,即定义了两个structstudent类型的变量stu1和stu2。这种在声明结构体类型的同时定义结构体变量一般形式为:

struct结构名

{

成员表列;

}变量名表列;3.直接定义结构类型变量

例如:

struct

{

intnum;

charname[19];

charsex;

intage;

chartel[20];

charaddr[40];

}stu1,stu2;其一般形式为:

struct

{

成员表列;

}变量名表列;说明:

(1)结构体类型和结构体变量是不同的概念,不要混淆。在定义时一般先定义一个结构体类型,然后再定义该结构体类型的变量。注意:只能对结构体变量赋值、运算、输出,而不能对结构体类型赋值、运算、输出。

(2)在编译时,对结构体类型不分配空间,只对变量分配空间。

(3)一个结构体变量所占存储空间是各个成员所占存储空间之和。例如,上面定义的stu1,stu2所占存储空间的大小为4+19+1+4+20+40,共计88个字节。

(4)结构体中的成员也可以是另一个结构体类型的变量,例如:

structdate

{

intyear;

intmonth;

intday;

};

structstudent

{

intnum;

charname[19];

charsex;

intage;

chartel[20];

charaddr[40];

structdatebirthday;

};注意:结构体成员的类型不能是正在定义的结构体类型(递归定义,结构体类型大小不能确定),但可以是正在定义的结构体类型的指针。

4.结构体变量的初始化

对结构体变量的各个成员赋初值,称为初始化。初始化的方法有以下几种。

(1)定义结构体变量时赋初值。例如:struct

{

intnum;

charname[19];

charsex;

intage;

chartel[20];

charaddr[40];

}stu1={};注意:变量后面的一组数据应该用“{}”括起来,其顺序也应该与结构体中的成员顺序保持一致。

(2)定义结构体变量后直接赋值。例如:

stu1.num=10036;

strcpy(,"zhangsan");

stu1.sex='M';

(3)用scanf()函数对结构体变量的成员赋值。例如:

scanf("%d%s%c%d%s",&stu1.num,,&stu1

.sex,&stu1.age,stu1.tel);

5.结构体变量的引用

在定义结构体变量以后,不能直接引用该变量,而应引用该变量的成员,格式如下:

结构体变量名.成员名

其中“.”是成员运算符,它在所有运算符中优先级最高,与()是同一个级别。

说明:

(1)不允许将结构体变量整体输入和输出,只能对结构体变量中的各个成员分别进行输入和输出。例如:

scanf("%d%s%c%d%s",&stu1.num,,

&stu1.sex,&stu1.age,stu1.tel);

printf("%d,%s,%c,%d,%s\n",stu1.num,,

stu1.sex,stu1.age,stu1.tel);

(2)只能对最低级的成员进行赋值、存取以及运算。例如:

stu1.birthday.year=1980;

(3)对结构体变量的成员可以像普通变量一样进行各种运算。同时也可以把结构体变量的成员看做简单变量来使用。例如:stu1.age可以等价于一个int型变量,如:

stu1.age++;

sum=stu1.age+stu2.age;

(4)同一种类型的结构体变量之间可以直接赋值(整体赋值,实质上是对对应成员逐个依次赋值的)。例如:

stu2=stu1;

6.结构体数组

数组是具有相同类型的一组元素的集合。结构体数组中的每一个元素都是结构体类型。结构体数组的定义方法与结构体变量的定义方法类似,结构体数组元素的使用也和一般数组元素的使用类似。在实际应用中,经常采用结构体数组来表示具有相同数据结构的一个群体,例如一个班的学生档案,一个车间的职工信息等。/*源程序9-1*/

#include"stdio.h"

main()

{

structstudent

{

intnum;

charname[19];

charsex;

intage;

chartel[20];

charaddr[40];

};

structstudentst[2]={{2001101,"wangchen",'M',23,

,"nanjing"},{2001105,"zhangsan",'F',21,,"xi'an"}};

printf("%d,%s,%c,%d,%s,%s\n",st[1].num,st[1].name,

st[1].sex,st[1].age,st[1].tel,st[1].addr);

}程序运行结果如下:

注意:上面的源程序中定义了结构体数组st,其元素为st[0]和st[1]。可以采用“数组元素.成员名”这种格式来使用结构体数组中某个元素的成员,例如,st[0].num和st[1].age。

7.结构体指针变量

结构体指针变量和一般指针变量的作用是一致的。结构体指针变量的值是结构体变量在内存中的起始地址。

(1)定义:

struct结构体名*结构体指针变量名;

例如:

structstudentstu;

structstudent*s=&stu;

structstudentst[3];

structstudent*p,*q;

p=st,q=&st[1];

(2)通过结构体指针变量访问结构体变量的成员方法为:

(*结构体指针变量名).成员名

或者

结构体指针变量名->成员名

其中“->”称为指向运算符。例如:

(*s).sex='M';

s->age=23;

结构体指针变量完整的应用程序举例如下:/*源程序9-2*/

#include"stdio.h"

main()

{

structstudent

{

intnum;

charname[19];

charsex;

intage;

chartel[20];

charaddr[40];

};

structstudentstu={2001101,"wangchen",'M',23,

,"nanjing"};

structstudent*s=&stu;

printf("%d,%s,%c,%d,%s,%s\n",stu.num,(*s).name,s->sex,s->age,(*s).tel,stu.addr);

}

程序运行结果如下: 9.2单链表

9.2.1动态存储分配

1.malloc函数

格式:void*malloc(unsignedintsize);

功能:在内存的动态存储区中分配1个长度为size的连续空间。

函数的返回值:申请存储空间成功,返回申请的存储空间的起始地址;申请不成功,返回NULL。

2.calloc函数

格式:void*calloc(unsignedn,unsignedintsize);

功能:在内存的动态存储区中分配n个长度为size的连续空间。

函数的返回值:申请存储空间成功,返回申请的存储空间的起始地址;申请不成功,返回NULL。

3.free函数

格式:voidfree(void*p);

功能:释放由指针p指向的内存区,使这部分内存区能被其他变量使用。

free函数无返回值。9.2.2单链表概述

链表是一种重要的数据结构,它可以根据需要动态地开辟内存单元,进行相应的操作(形象地讲,就像是根据人数登记房间住宿)。链表就像一列火车,有车头,有车尾,每节车厢里都放有一定数量的货物,而且从中增加、删除若干节车厢后,还要保证前后的连接。

(1)链表有一个“头指针”变量,它存放链表第一个结点的地址。

(2)链表中每一个元素称为一个结点,每个结点都包括两部分:数据域和指针域。数据域用来存放用户数据,指针域用来存放下一个结点的地址。

(3)链表的最后一个结点的指针域常常设置为NULL(空),表示链表到此结束。

(4)常常用结构体变量作为链表中的结点。

单链表的结构如图9-1所示。图9-1单链表的结构9.2.3单链表的基本操作

1.建立动态链表

建立动态链表是指在程序执行过程中根据需要从无到有地建立一个链表,即不断地一个一个开辟结点空间并输入各结点数据,同时建立各个结点的前后相连关系。/*源程序9-3*/

#include"stdio.h"

#include"malloc.h"

#defineLENsizeof(structstudent)

intn;

structstudent

{

intnum;

charname[20];

structstudent*next;};

structstudent*creat()

{

structstudent*head;//head为头指针,指向链表的第一个结点

structstudent*p1,*p2;//p1指向当前申请的空间,p2指向链表的最后结点

n=0;

head=NULL;

p1=(structstudent*)malloc(LEN);

p2=p1;

scanf("%d,%s",&p1->num,p1->name);

while(p1->num!=0)

{

n=n+1;

if(n==1)head=p1;

elsep2->next=p1;

p2=p1;

p1=(structstudent*)malloc(LEN);

scanf("%d,%s",&p1->num,p1->name);

}

p2->next=NULL;

returnhead;

}程序说明:

(1)定义一个结构体,包含数据域(学号、姓名)和指针域(指向结构体变量的指针)。定义全局变量n,统计结点的个数。

(2)动态申请一个结点,输入学生数据到此结点空间,并使p1和p2共同指向它。

(3)输入学生数据到p1所指向的空间。

(4)在循环控制下,动态再申请一个存储空间,使p1指向它,然后输入学生数据,结点个数增1,n=n+1。

(5)执行p2->next=p1;语句,实现链表的链接。

(6) p1指向当前申请的结点,p2始终指向链表的最后结点。

(7)循环执行(4)~(6),实现结点的动态创建。

(8)循环的条件是申请的存储空间中输入的学号不等于0。

(9)循环结束,使最后一个结点的指针域为NULL。

建立单链表的过程如图9-2所示。图9-2单链表的建立过程

2.输出动态链表

输出链表就是将链表各个结点的数据在循环控制下,依次从单链表头输出到链表尾。/*源程序9-4*/

voidprint(structstudent*head)

{

structstudent*p;

printf("\nNow,There%drecordsare:\n",n);

p=head;

if(head!=NULL)

do

{

printf("%-6d%-10s\n",p->num,p->name);

p=p->next;

}while(p!=NULL);

}程序说明:

(1)首先定义一个结构体指针p,并使p指向链表的第一个结点。

(2)在链表非空的情况下,输出p所指向的结点的数据。

(3)使p指针移到下一个结点,通过语句p=p->next; 实现。

(4)循环执行(2)、(3),循环条件是p所指向的结点不为NULL,即p!=NULL。

3.链表的插入操作

链表的插入指的是将某个结点插入到一个按照学号已经有序的链表中,插入后链表仍然保持有序。/*源程序9-5*/

structstudent*insert(structstudent*head,structstudent*stu)

{

structstudent*p0,*p1,*p2;

p1=head;

p0=stu;

if(head==NULL)

{

head=p0;

p0->next=NULL;

}

else

{

while((p0->num>p1->num)&&(p1->next!=NULL))

{

p2=p1;

p1=p1->next;

}

if(p0->num<p1->num)

{ if(head==p1)

head=p0;

else

p2->next=p0;

p0->next=p1;

}

else

{ p1->next=p0;

p0->next=NULL;

}

}

n=n+1;

returnhead;

}程序说明:

(1)此链表的插入操作前提是链表已经按照学号(num)的大小关系由小到大排好序。

(2)先用指针变量p0指向待插入的结点,p1指向当前结点,初始情况为链表的第1个结点。

(3)在空表中插入结点,或者如果要插入结点比第1个结点学号还小,则直接执行head=p0;p0->next=p1;。

(4)将要插入结点学号与当前结点学号进行比较。在循环控制下,逐个比较if(p0->num>p1->num),如果成立,则向后查找合适位置p2=p1;,p1指针下移一个结点p1=p1->next;如果不成立,则插入,执行p2->next=p0;p0->next=p1;,即将p0指向的结点插入到当前结点之前。

(5)如果要插入的结点学号比最后一个结点(p1->next=

=NULL)学号还大,则插在最后一个结点之后,p1->next=p0;p0->next=NULL;。

4.链表的删除操作

链表的删除是指将某个结点从一个有序的链表中分离出来,删除后链表仍然保持有序。/*源程序9-6*/

structstudent*del(structstudent*head,intno)

{

structstudent*p1,*p2;

if(head==NULL)

{

printf("\nlistnull!\n");

returnhead;

}

p1=head;

while(p1->num!=no&&p1->next!=NULL){

p2=p1;

p1=p1->next;

}

if(p1->num==no)

{

if(p1==head)

head=p1->next;

else

p2->next=p1->next;

printf("delete:%d\n",no);

n=n-1;

}

else

printf("%dnotbeenfound!\n",no);

returnhead;

}程序说明:

(1)设两个指针变量p1和p2,使p1指向链表的第一个结点。

(2)如果该链表为空链表,则无需做任何操作,提前结束删除。

(3)如果要删除的不是第一个结点,则在循环控制下,逐个比较检查。

while((p1->num!=no)&&(p1->next!=NULL)),如果成立,p2=p1;则使p2指向刚才检查过的那个结点,便于删除;p1指针下移一个结点,p1=p1->next;。如果不成立,则退出循环,表示找到了或者整个链表全部检查完都没找到。

(4)如果要删的是第一个结点,则应将p1->next赋给head,这时head指向原来第二个结点,原来第一个结点已与链表脱离而“丢失”;如果要删的不是第一个结点,则将p1->next赋给p2->next。

(5)如果链表中找不到要删除的结点,则输出错误信息提示。

5.链表的综合应用

例9-2

将以上建立、输出、插入、删除的函数组织在一个程序中,并在main()函数中进行调用,以实现链表的综合应用。/*源程序9-7*/

main()

{

structstudent*head,*stu;

intdel_num;

printf("inputrecords:\n");

head=creat();

print(head);

printf("\ninputthedeletednumber:\n");

scanf("%d",&del_num);

while(del_num!=0)

{

head=del(head,del_num);

print(head);

printf("inputthedeletednumber:\n");

scanf("%d",&del_num);

}

printf("\ninputtheinsertedrecord:\n");

stu=(structstudent*)malloc(LEN);

scanf("%d,%s",&stu->num,stu->name);

while(stu->num!=0)

{

head=insert(head,stu);

print(head);

printf("inputtheinsertedrecord:\n");

stu=(structstudent*)malloc(LEN);

scanf("%d,%s",&stu->num,stu->name);

}

}程序运行结果如下: 9.3共用体和枚举类型

9.3.1共用体

1.定义

共用体就是将不同类型的数据项存放于同一段内存单元的一种构造数据类型。与结构体类似,在共用体内可以定义多种不同数据类型的成员。两者之间的区别在于:共用体类型变量所有成员共用一块内存单元,虽然每个成员都可以被赋值,但只有最后一次赋予的成员值能够保存且有意义,前面赋予的成员值被后面赋予的成员值所覆盖;结构体中各个成员占有各自的内存空间,各个成员的赋值互不影响。2.定义共用体类型变量的一般形式

定义共用体类型变量的一般形式如下:

union共用体名

{

类型1成员1;

类型2成员2;

类型n成员n;

}变量表列;例如:

uniondata

{

inti;

floatf;

charch;

}data1,data2;

3.说明

(1)结构体变量所占内存长度是各个成员占的内存长度之和,每个成员分别占有各自独立的内存单元;而共用体变量所占的内存长度等于最长的成员的长度。

(2)在共用体中,同一内存段可以用来存放不同类型的成员,但是每一瞬时只能存放其中的一种(也只有一种有意义)。

(3)共用体变量的地址和其成员的地址都是同一地址。

(4)不能对共用体变量名赋值,也不能企图引用共用体变量名得到一个值。同时,也不能在定义共用体变量时对其进行初始化(系统不清楚是为哪个成员赋初值)。

(5)不能把共用体变量作为函数参数,也不能使函数带回共用体变量,但可以使用指向共用体变量的指针。

(6)共用体和结构体可以相互嵌套。

例9-3

统计学校的人员数据。教师的数据包括:编号、姓名、性别、职业、职称;学生的数据包括:编号、姓名、性别、职业、班级。如果将两种数据放在同一个表格中,那么其中有一栏,对于教师登记教师的“职称”,对于学生则登记学生的“班级”(同一人员不可能既是教师又是学生)。解决此问题的程序如下:/*源程序9-8*/

#include<stdio.h>

unionuniondata

{

charclasses[20];

charposition[20];

};

structdata

{

intnum;

charname[20];charsex;

charjob;

unionuniondatasharedata;

};

voidmain()

{

structdataperson[2];

inti;

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

{

scanf("%d%s%c%c",&person[i].num,&person[i].name,

&person[i].sex,&person[i].job);

if(person[i].job=='S')

scanf("%s",person[i].sharedata.classes);

elseif(person[i].job=='T')

scanf("%s",person[i].sharedata.position);

else

printf("Inputerror!");

}

printf("\n");

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

{

if(person[i].job=='S')

printf("No:%-6dName:%-10ssex:%cjob:%csharedata:

%-8s\n",person[i].num,

person[i].name,person[i].sex,person[i].job,person

[i].sharedata.classes);

else

printf("No:%-6dName:%-10ssex:%cjob:%csharedata:

%-8s\n",person[i].num,

person[i].name,person[i].sex,person[i].job,person[i].

sharedata.position);

}

}程序运行结果如下:9.3.2枚举类型

(1)枚举类型是指将变量的值一一列举出来,变量的值只限于列举出来的值的范围。

(2)声明枚举类型的格式:

enum枚举类型名{枚举常量1,枚举常量2,…,枚举常量n};

例如:

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

(3)定义枚举类型变量。

①定义枚举类型的同时定义变量:

enum枚举类型名{枚举常量1,…}枚举变量名;

②先定义类型后定义变量:

enum枚举类型名枚举变量名;

③匿名枚举类型:

enum{枚举常量列表}枚举变量列表;

(4)说明:

①enum是标识枚举类型的关键字,定义枚举类型时用enum开头。

②枚举常量是符号,由程序设计者自己指定,命名规则同标识符。使用枚举常量可以提高程序的可读性。枚举类型在可视化编程时常使用。

③枚举元素在编译时,按定义时的排列顺序取值0,1,2,…(类似整型常数)。在定义枚举类型时,可以给这些枚举常量指定整型常数值(未指定值的枚举常量的值是前一个枚举常量的值加1)。④枚举元素是常量,不是变量。可以将枚举元素赋值给枚举变量,但是不能给枚举常量赋值。

⑤枚举常量不是字符串。

⑥枚举变量、常量一般可以参与整数可以参与的运算,如算术/关系/赋值等运算。

例如:

enumweekday{sun=7,mon=1,tue,wed,thu,fri,sat};

9.4typedef定义类型

C语言不仅提供了标准的数据类型名,而且还允许由用户为已经存在的数据类型名取“别名”。用typedef对已有的数据类型名取“别名”的一般格式为:

typedef数据类型名数据类型的别名;

数据类型的别名一般用大写表示,例如:

typedefintINTEGER;

INTEGERa,b;

以上语句等效于:

inta,b;说明:typedef并没有建立新的数据类型,仅仅是对已有类型命名别名。使用typedef定义类型可以增加程序可读性,简化书写。

用typedef对已有的数据类型名取“别名”的一般方法为:

①先按定义变量的方法写出定义体(如:inti)。

②将变量名换成新类型名(如:将i换成COUNT)。

③在最前面加typedef(例如:typedefintCOUNT)。

④然后可以用此类型别名去定义变量。例如:

typedefcharNAME[20];

NAME代表了字符数组类型,数组长度为20。然后可以用NAME定义变量,如:

NAMEa1,a2,s1,s2;

以上命令等效于:

chara1[20],a2[20],s1[20],s2[20];例如:

typedefstructstudent

{

intnum;

charname[19];

charsex;

intage;

}STU;

STU代表了上面的一个结构体类型,然后可以用STU来定义此结构变量,如:

STUstudent1,student2;

以上命令等效于:

structstudentstudent1,student2;

例如:

typedefchar*STRING;

STRING代表了字符指针类型,然后可以用STRING来定义字符指针类型的变量,如:

STRINGc;

以上命令等效于:

char*c;

习题

1.学生的记录由学号和成绩组成,共N名学生。编写函数fun(STU*a,STU*b,intlow,inthight)。它的功能是:把a数组中指定分数范围内的学生数据放在b所指的数组中,分数范围内的学生人数由函数值返回。分数范围从最低分的low开始,到最高分hight结束,并且包含这两个上下界限。typedefstruct

{

charnum[10];

ints;

}STU;

intfun(STU*a,STU*b,intlow,inthight)

{}

2.学生的记录由学号和学习成绩构成,共N名学生。编写函数fun。函数的功能是:找出成绩最低的学生记录,通过形参返回主函数(规定只有一个最低分)。

typedefstructss

{

charnum[10];

int

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论