第9章结构和杂类精.docx
- 文档编号:7299926
- 上传时间:2023-01-22
- 格式:DOCX
- 页数:27
- 大小:32.84KB
第9章结构和杂类精.docx
《第9章结构和杂类精.docx》由会员分享,可在线阅读,更多相关《第9章结构和杂类精.docx(27页珍藏版)》请在冰豆网上搜索。
第9章结构和杂类精
第9章结构和杂类
教学目的和要求:
掌握构造类型中的结构体的定义和使用,掌握链表的建立、删除及插入
教学重点:
有关链表的操作
课时安排:
6+4
教学内容:
9.1 概述
将不同类型的数据组合成一个有机的整体,以便于引用。
这些组合在一个整体中的数据是互相联系的。
在一个组合项中包含若干个类型不同(当然也可以相同)的数据项。
C语言允许用户自己指定这样一种数据结构,它称为结构体(structure)把它们组织成一个组合项,。
它相当于其他高级语言中的“记录”。
struct student
{intnum;
char name[20];
char sex;
int age;
float score;
char addr[30];
};
上面由程序设计者指定了一个新的结构体类型structstudent(struct是声明结构体类型时所必须使用的关键字,不能省略),它向编译系统声明这是一个“结构体类型”,它包括num、name、sex、age、score、addr等不同类型的数据项。
structstudent是一个类型名,它和系统提供的标准类型(如int、char、float、double等)一样具有同样的地位和作用,都可以用来定义变量的类型,只不过结构体类型需要由用户自己指定而已。
声明一个结构体类型的一般形式为
struct结构体名
{成员表列};
“
可以把“成员表列”称为“域表”。
每一个成员也称为结构体中的一个域。
成员名定名规与变量名同。
9.2 定义结构体类型变量的方法
1. 先声明结构体类型再定义变量名
如上面已定义了一个结构体类型structstudent,可以用它来定义变量。
如:
structstudentstudent1,student2
结构体类型名结构体变量名;
应当注意,将一个变量定义为标准类型(基本数据类型)与定义为结构体类型不同之处在于后者不仅要求指定变量为结构体类型,而且要求指定为某一特定的结构体类型(例如structstudent类型)。
因为可以定义出许许多多种具体的结构体类型。
而在定义变量为整型时,只需指定为int型即可。
2. 在声明类型的同时定义变量
例如:
struct student
{int num;
char name[20];
char sex;
int age;
floatscore;
char addr[30];
}student1,student2;
它的作用与第一种方法相同,即定义了两个structstudent类型的变量student1、student2。
这种形式的定义的一般形式为
struct 结构体名
{
成员表列
}变量名表列;
3. 直接定义结构类型变量
其一般形式为
struct
{
成员表列
}变量名表列;
关于结构体类型,有几点要说明:
(1)类型与变量是不同的概念,不要混同。
只能对变量赋值、存取或运算,而不能对一个类型赋值、存取或运算。
在编译时,对类型是不分配空间的,只对变量分配空间。
(2)对结构体中的成员(即“域”),可以单独使用,它的作用与地位相当于普通变量。
(3)成员也可以是一个结构体变量。
如:
struct date/*声明一个结构体类型*/
{int month;
int day;
int year;
};
struct student
{int num;
char name[20];
char sex;
int age;
structdatebirthday;/*birthday是structdate类型*/
char addr[30];
}student1,student2;
先声明一个structdate类型,它代表“日期”,包括3个成员:
month(月)、day(日)、year(年)。
然后在声明structstudent类型时,将成员birthday 指定为structdate类型。
structstudent的结构见图11.3所示。
已声明的类型structdate与其他类型(如int,char)一样可以用来定义成员的类型。
(4)成员名可以与程序中的变量名相同,二者不代表同一对象。
例如,程序中可以另定义一个变量num,它与structstudent中的num是两回事,互不干扰。
9.3 结构体变量的引用
在定义了结构体变量以后,当然可以引用这个变量。
但应遵守以下规则:
(1)不能将一个结构体变量作为一个整体进行输入和输出。
例如,已定义student1和student2为结构体变量并且它们已有值。
不能这样引用:
printf("%d,%s,%c,%d,%f,%s\n",student1);
(2)如果成员本身又属一个结构体类型,则要用若干个成员运算符,一级一级地找到最低的一级的成员。
只能对最低级的成员进行赋值或存取以及运算。
例如,对上面定义的结构体变量student1,可以这样访问各成员:
student1.num
student1.birthday.month
注意:
不能用student1.birthday来访问student1变量中的成员birthday,因为birthday本身是一个结构体变量。
(3)对结构体变量的成员可以像普通变量一样进行各种运算(根据其类型决定可以进行的运算)。
例如:
student2.score=student1.score;
sum=student1.score+student2.score;
student1.age++;
++student1.age;
(4)可以引用结构体变量成员的地址,也可以引用结构体变量的地址。
如:
scanf("%d",&student1.num);(输入student1.num的值)printf("%o",&student1);(输出student1的首地址)但不能用以下语句整体读入结构体变量,如:
scanf("%d,%s,%c,%d,%f,%s",&student1);
结构体变量的地址主要用于作函数参数,传递结构体的地址。
9.4 结构体变量的初始化
和其他类型变量一样,对结构体变量可以在定义时指定初始值。
例9.1对结构体变量初始化。
main()
{structstudent
{longintnum;
charname[20];
charsex;
charaddr[20];
}a={89031,"LiLin",'M',"123BeijingRoad"};
printf("NO.:
%ld\nname:
%s\nsex:
%c\naddress:
%s\n",a.num,a.name,a.sex,a.addr);
}
运行结果如下:
No.:
89031
name:
LiLin
sex:
M
address:
123BeijingRoad
9.5 结构体数组
9.5.1 定义结构体数组
和定义结构体变量的方法相仿,只需说明其为数组即可。
如:
structstudent
{intnum;
charname[20];
charsex;
intage;
floatscore;
charaddr[30];
};
structstudent stu[3];
以上定义了一个数组stu,其元素为structstudent类型数据,数组有3个元素。
也可以直接定义一个结构体数组,如:
structstudent
{intnum;
…
}stu[3];
或
struct
{intnum;
…
}stu[3];
9.5.2结构体数组的初始化与其他类型的数组一样,对结构体数组可以初始化。
如:
struct student
{int num;
char name[20];
char sex;
int age;
float score;
char add[30];
}
stu[3]={{10101,“LiLin”,‘M’,18,87.5,“103BeijingRoad”},{10102,“ZhangFun”,‘M’,19,99,“130ShanghaiRoad”},{10104,“WangMin”,‘F’,20,78.5,“1010ZhongshanRoad”}};
定义数组stu时,元素个数可以不指定,即写成以下形式:
stu[ ]={…},{…},{…};编译时,系统会根据给出初值的结构体常量的个数来确定数组元素的个数。
当然,数组的初始化也可以用以下形式:
struct student
{int num;
…
};
struct student stu[ ]={{…},{…},{…}};
即先声明结构体类型,然后定义数组为该结构体类型,在定义数组时初始化。
从以上可以看到,结构体数组初始化的一般形式是在定义数组的后面加上:
={初值表列};
9.5.3 结构体数组应用举例
下面举一个简单的例子来说明结构体数组的定义和引用。
例:
对候选人得票的统计程序。
设有3个候选人,每次输入一个得票的候选人的名字,要求最后输出各人得票结果。
程序如下:
#include
structperson
{charname[20];
intcount;
}
leader[3]={"Li",0,"Zhang",0,"Fun",0};
main()
{int i,j;
char leader-name[20];
for (i=1;i<=10;i++)
{scanf("%s",leader-name);
for(j=0;j<3;j++)
if(strcmp(leader-name,leader[j].name)==0)leader[j].count++:
}
printf("\n");
for(i=0;i<3;i++)
printf("%5s:
%d\n",leader[i].name,leader[i].count);
}
运行情况如下:
Li
Li
Fun
Zhang
Zhang
Fun
Li
Fun
Zhang
Li
Li∶4
Zhang∶3
Fun∶3
9.6 指向结构体类型数据的指针
一个结构体变量的指针就是该变量所占据的内存段的起始地址。
可以设一个指针变量,用来指向一个结构体变量,此时该指针变量的值是结构体变量的起始地址。
指针变量也可以用来指向结构体数组中的元素。
9.6.1 指向结构体变量的指针
下面通过一个简单例子来说明指向结构体变量的指针变量的应用。
例9.3指向结构体变量的指针的应用。
#include
main()
{struct student
{long num;
char name[20];
charsex;
floatscore;
};
struct student stu-1;
struct student *p;
p=&stu-1;
stu-1.num=89101;
strcpy(stu-1.name,"LiLin");
stu-1.sex='M';
stu-1.score=89.5;
printf("No.:
%ld\nname:
%s\nsex:
%c\nscore:
%f\n",stu-1.num,stu-1.name,stu-1.sex,stu-1.score);
printf("No.:
%ld\nname:
%s\nsex:
%c\nscore:
%f\n",(*p).num,(*p).name,(*p).sex,(*p).score);
}
程序运行结果如下:
No.:
89101
name:
LiLin
sex:
M
score:
89.500000
No:
89101
name:
LiLin
sex:
M
score:
89.500000
在C语言中,为了使用方便和使之直观,可以把(*p).num改用p—>num来代替,它表示*p所指向的结构体变量中的num成员。
同样,(*p).name等价于p—>name。
也就是说,以下三种形式等价:
①结构体变量.成员名
②(*p).成员名
③p->成员名
9.6.2 指向结构体数组的指针
structstudent
{intnum;
charname[20];
charsex;
intage;
};
structstudentstu[3]={{10101,“LiLin”,‘M’,18},{10102,“ZhangFun”,‘M’,19},{10104,“WangMin”,‘F’,20}};
main()
{structstudent*p;
printf("No.Namesexage\n");
for (p=stu;p<stu+3;p++)
printf("%5d%-20s%2c%4d\n",p->num,p->name,p->sex,p->age);
}
运行结果如下:
No. Name sexage
10101 LiLin M 18
10102 ZhangFunM 19
10104 WangMinF 20
9.7 用指针处理链表
链表有一个“头指针”变量,链表中每一个元素称为“结点”,每个结点都应包括两个部分:
一为用户需要用的实际数据,二为下一个结点的地址。
可以看到,这种链表的数据结构,必须利用指针变量才能实现。
即:
一个结点中应包含一个指针变量,用它存放下一结点的地址。
struct student
{int num;
float score;
struct studentnext;
};
9.7.2 简单链表
下面通过一个例子来说明如何建立和输出一个简单链表。
例11.7建立一个如图11.11所示的简单链表,它由3个学生数据的结点组成。
输出各结点中的数据。
#defineNULL0
structstudent
{longnum;
floatscore;
structstudent*next;
};
main()
{structstudenta,b,c,*head,*p;
a.num=99101;a.score=89.5;
b.num=99103;b.score=90;
c.num=99107;c.score=85;/*对结点的num和score成员赋值*/
head=&a;/*将结点a的起始地址赋给头指针head*/
a.next=&b;/*将结点b的起始地址赋给a结点的next成员*/
b.next=&c;/*将结点c的起始地址赋给b结点的next成员*/
c.next=NULL;/*c结点的next成员不存放其他结点地址*/
p=head;/*使p指针指向a结点*/
do
{printf("%ld%5.1f\n",p->num,p->score);/*输出p指向的结点的数据*/
p=p->next;/*使p指向下一结点*/
}while(p!
=NULL);/*输出完c结点后p的值为NULL*/
}
9.7.3 输出链表
将链表中各结点的数据依次输出。
要知道head的值。
然后设一个指针变量p,先指向第一个结点,输出p所指的结点,然后使p后移一个结点,再输出。
直到链表的尾结点。
例:
编写一个输出链表的函数print。
voidprint(structstudent*head)
{structstudent*p;
printf("\nNow,These%drecordsare:
\n",n);
p=head;
if(head!
=NULL)
do
{printf("%ld%5.1f\n",p->num,p->score);
p=p->next;
}while(p!
=NULL);
}
9.7.4 对链表的删除操作
与此相仿,从一个动态链表中删去一个结点,并不是真正从内存中把它抹掉,而是把它从链表中分离开来,只要撤消原来的链接关系即可。
删除结点的函数del如下:
structstudentdel(structstudent*head,longnum)
{structstudent*p1,*p2;
if(head==NULL){printf("\nlistnull!
\n");return(head);}
p1=head;
while(num!
=p1->num&&p1->next!
==NULL)/*p1指向的不是所要找的结点,并且后面还有结点/
{p2=p1;p1=p1->next;}/p1后移一个结点*/
if(num==p1->num)/找到了*/
{if(p1==head)head=p1->next;/若p1指向的是首结点,把第二个结点地址赋予head/
elsep2->next=p1->next;
/*否则将下一结点地址赋给前一结点地址*/
printf("delete:
%ld\n",num);
n=n-1;
}
elseprintf("%ldnotbeenfound!
\n",num);/找不到该结点/
return(head);
}
9.7.5 对链表的插入操作
例:
插入结点的函数insert如下。
structstudent*insert(structstudent*head,structstudent*stud)
{structstudent*p0,*p1,*p2;
p1=head;/使p1指向第一个结点/
p0=stud;/p0指向要插入的结点/
if(head==NULL) /原来的链表是空表/
{head=p0;p0->next=NULL;}/*使p0指向的结点作为头结点*/
else
{while((p0->num>p1->num)&&(p1->next!
=NULL))
{p2=p1;/*使p2指向刚才p1指向的结点*/
p1=p1->next;}/*p1后移一个结点*/
if(p0->num<p1->num)
{if(head==p1)head=p0; /插到原来第一个结点之前*/
elsep2->next=p0;
/插到p2指向的结点之后*/
p0->next=p1;}
else
{p1->next=p0;p0->next=NULL;}}/*插到最后的结点之后*/
n=n+1; /结点数加1*/
return(head);
}
函数参数是head和stud。
stud也是一个指针变量,从实参传来待插入结点的地址给stud。
语句p0=stud的作用是使p0指向待插入的结点。
函数类型是指针类型,函数值是链表起始地址head。
9.7.6 对链表的综合操作
将以上建立、输出、删除、插入的函数组织在一个C程序中,即将例11.8~11.11中的4个函数顺序排列,用main函数作主调函数。
可以写出以下main函数(main函数的
位置在以上各函数的后面)。
main()
{structstudent*head,stu;
longdel-num;
printf("inputrecords:
\n");
head=creat();/返回头指针*/
print(head);/输出全部结点*/
printf("\ninputthedeletednumber:
");
scanf("%ld",&del-num);/输入要删除的学号*/
head=del(head,del-num);/删除后链表的头地址/
print(head);/输出全部结点*/
printf("\ninputtheinsertedrecord:
");/输入要插入的结点*/
scanf("%ld,%f",&stu.num,&stu.score);
head=insert(head,&stu);/返回地址/
print(head);/*输出全部结点*/
}
此程序运行结果是正确的。
它只删除一个结点,插入一个结点。
但如果想再插入一个结点,重复写上程序最后4行,共插入两个结点。
运行结果却是错误的。
运行结果如下:
inputrecords:
(建立链表)
99101,90
99103,98
99105.76
0,0
Now,These3recordsare:
99101 90.0
99103 98.0
99105 76.0
inputthedeletednumber:
99103(删除)
delete:
99103
Now,These2recordsare:
99101 90.099105 76.0
inputtheinsertedrecord:
99102,90(插入第一个结点)
Now,These3recordsare:
99101 90.0
99102 90.0
99105 76.0
inputtheinsertedrecord:
89104,99(插入第二个结点)
Now,These4recordsare:
99101 90.0
99104 99.0
99104 99.0
99104 99.0
…
(无终止地输出99104的结点数据)
出现以上结果的原因是stu是一个有固定地址的结构体变量。
第一次把stu结点插入到链表中。
第二次若再用它来插入第二个结点,就把第一次结点的数据冲掉了。
实际上并没有开辟两个结点。
main 函数如下:
main()
{structstudent*head,*stu;
longdel-num;
printf("inputrecords:
\n");
head=creat();
print(head);
printf("\ninputthedeletednumber:
");
scanf("%ld",&del-num);
while(del-num!
=0)
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 结构 杂类精