第9章结构和杂类精Word文档格式.docx
《第9章结构和杂类精Word文档格式.docx》由会员分享,可在线阅读,更多相关《第9章结构和杂类精Word文档格式.docx(27页珍藏版)》请在冰豆网上搜索。
struct 结构体名
{
成员表列
}变量名表列;
3. 直接定义结构类型变量
其一般形式为
struct
{
成员表列
}变量名表列;
关于结构体类型,有几点要说明:
(1)类型与变量是不同的概念,不要混同。
只能对变量赋值、存取或运算,而不能对一个类型赋值、存取或运算。
在编译时,对类型是不分配空间的,只对变量分配空间。
(2)对结构体中的成员(即“域”),可以单独使用,它的作用与地位相当于普通变量。
(3)成员也可以是一个结构体变量。
如:
struct date/*声明一个结构体类型*/
{int month;
int day;
int year;
};
{int num;
structdatebirthday;
/*birthday是structdate类型*/
先声明一个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的首地址)但不能用以下语句整体读入结构体变量,如:
%d,%s,%c,%d,%f,%s"
结构体变量的地址主要用于作函数参数,传递结构体的地址。
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;
intage;
charaddr[30];
};
structstudent stu[3];
以上定义了一个数组stu,其元素为structstudent类型数据,数组有3个元素。
也可以直接定义一个结构体数组,如:
structstudent
…
}stu[3];
或
…
9.5.2结构体数组的初始化与其他类型的数组一样,对结构体数组可以初始化。
struct student
{int num;
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 stu[ ]={{…},{…},{…}};
即先声明结构体类型,然后定义数组为该结构体类型,在定义数组时初始化。
从以上可以看到,结构体数组初始化的一般形式是在定义数组的后面加上:
={初值表列};
9.5.3 结构体数组应用举例
下面举一个简单的例子来说明结构体数组的定义和引用。
例:
对候选人得票的统计程序。
设有3个候选人,每次输入一个得票的候选人的名字,要求最后输出各人得票结果。
程序如下:
#include<
string.h>
structperson
{charname[20];
intcount;
}
leader[3]={"
Li"
,0,"
Zhang"
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;
printf("
%5s:
%d\n"
,leader[i].name,leader[i].count);
运行情况如下:
Li
Li
Fun
Zhang
Li∶4
Zhang∶3
Fun∶3
9.6 指向结构体类型数据的指针
一个结构体变量的指针就是该变量所占据的内存段的起始地址。
可以设一个指针变量,用来指向一个结构体变量,此时该指针变量的值是结构体变量的起始地址。
指针变量也可以用来指向结构体数组中的元素。
9.6.1 指向结构体变量的指针
下面通过一个简单例子来说明指向结构体变量的指针变量的应用。
例9.3指向结构体变量的指针的应用。
{struct student
{long num;
struct student stu-1;
struct student *p;
p=&
stu-1;
stu-1.num=89101;
strcpy(stu-1.name,"
stu-1.sex='
;
stu-1.score=89.5;
printf("
No.:
%c\nscore:
%f\n"
,stu-1.num,stu-1.name,stu-1.sex,stu-1.score);
,(*p).num,(*p).name,(*p).sex,(*p).score);
程序运行结果如下:
No.:
89101
LiLin
M
score:
89.500000
No:
89101
name:
sex:
在C语言中,为了使用方便和使之直观,可以把(*p).num改用p—>num来代替,它表示*p所指向的结构体变量中的num成员。
同样,(*p).name等价于p—>name。
也就是说,以下三种形式等价:
①结构体变量.成员名
②(*p).成员名
③p->
成员名
9.6.2 指向结构体数组的指针
{intnum;
};
structstudentstu[3]={{10101,“LiLin”,‘M’,18},{10102,“ZhangFun”,‘M’,19},{10104,“WangMin”,‘F’,20}};
{structstudent*p;
printf("
No.Namesexage\n"
for (p=stu;
p<stu+3;
p++)
%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
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"
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)
printf("
\nNow,These%drecordsare:
,n);
p=head;
if(head!
=NULL)
do
,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!
);
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->
/*否则将下一结点地址赋给前一结点地址*/
printf("
delete:
%ld\n"
,num);
n=n-1;
}
elseprintf("
%ldnotbeenfound!
,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->
=NULL))
{p2=p1;
/*使p2指向刚才p1指向的结点*/
p1=p1->
}/*p1后移一个结点*/
if(p0->
num<p1->
num)
{if(head==p1)head=p0;
/插到原来第一个结点之前*/
elsep2->
next=p0;
/插到p2指向的结点之后*/
p0->
next=p1;
{p1->
}}/*插到最后的结点之后*/
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函数的
位置在以上各函数的后面)。
{structstudent*head,stu;
longdel-num;
inputrecords:
head=creat();
/返回头指针*/
print(head);
/输出全部结点*/
\ninputthedeletednumber:
"
scanf("
%ld"
del-num);
/输入要删除的学号*/
head=del(head,del-num);
/删除后链表的头地址/
\ninputtheinsertedrecord:
/输入要插入的结点*/
%ld,%f"
stu.num,&
stu.score);
head=insert(head,&
stu);
/返回地址/
/*输出全部结点*/
此程序运行结果是正确的。
它只删除一个结点,插入一个结点。
但如果想再插入一个结点,重复写上程序最后4行,共插入两个结点。
运行结果却是错误的。
(建立链表)
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(插入第一个结点)
99101 90.0
99102 90.0
99105 76.0
89104,99(插入第二个结点)
Now,These4recordsare:
99104 99.0
…
(无终止地输出99104的结点数据)
出现以上结果的原因是stu是一个有固定地址的结构体变量。
第一次把stu结点插入到链表中。
第二次若再用它来插入第二个结点,就把第一次结点的数据冲掉了。
实际上并没有开辟两个结点。
main 函数如下:
{structstudent*head,*stu;
while(del-num!
=0)