610 likes | 763 Views
第 11 章 结构体与共用体. 信息管理系. 第 11 章 结构体与共用体. 11.1 结构体类型的定义与引用 11.2 链表的知识 11.3 共用体类型的定义与引用 11.4 枚举类型与自定义类型的定义. 结构体类型的定义. 在程序中,如需要存储一组性质相关、而类型不同的数据时,就可以使用结构体类型。 结构类型定义的一般形式: struct 结构类型名 { 成员类型 1 成员名 1 ; 成员类型 2 成员名 2 ; …… 成员类型 n 成员名 n ; };. 说明:
E N D
第11章 结构体与共用体 信息管理系
第11章 结构体与共用体 • 11.1 结构体类型的定义与引用 • 11.2 链表的知识 • 11.3 共用体类型的定义与引用 • 11.4 枚举类型与自定义类型的定义
结构体类型的定义 在程序中,如需要存储一组性质相关、而类型不同的数据时,就可以使用结构体类型。 结构类型定义的一般形式: struct 结构类型名 { 成员类型1 成员名1; 成员类型2 成员名2; …… 成员类型n 成员名n; }; • 说明: • struct是结构类型定义的保留字,必须原样照写; • 结构类型名应符合标识符的规定,在以后的结构变量声明中可以被引用; • 大括号内是由结构成员组成的结构体,每个成员的数据类型可以是基本类型,也可以是构造类型。 • 不要漏掉分号。
结构体类型的定义 例:一个学生的个人信息包括学号(int)、姓名(char)、性别(char)、年龄(int)、成绩(float)、家庭住址(char)等,因为这些数据是紧密联系的,所以我们需要将这些不同类型的数据组合成一个有机的整体,以便于引用和处理。我们可声明如下的结构体类型以满足我们的要求: struct student { int num ; char name[20] ; char sex ; int age ; float score ; char addr[30] ; } ; 这样我们就指定了一个结构体类型,相当于一个模型(相当于int,char等),但其中并无具体的数据,为了能够在程序中使用这种类型的数据,应当定义结构体类型的变量,并在其中存放具体数据。
结构体类型变量的定义 • 先声明结构体类型再定义变量名 • struct 结构体类型名 变量名; • 在声明类型的同时定义变量 • struct 结构体类型名 • {结构体成员列表; • }变量表; • 直接定义结构体类型变量 • struct • {结构体成员列表; • }变量表;
结构体类型变量的定义 要定义前面我们讨论的关于学生情况的结构体类型变量stu1、stu2,我们可用如下的三种方法来定义: 1 struct student { int num ; char name[20] ; char sex ; int age ; float score ; char addr[30] ; } ; struct student stu1,stu2; 2 struct student { int num ; char name[20] ; char sex ; int age ; float score ; char addr[30] ; } stu1,stu2; 3 struct student { int num ; char name[20] ; char sex ; int age ; float score ; char addr[30] ; } stu1,stu2;
结构体类型变量的初始化(赋值) 和其他类型变量一样,对结构体变量可以在定义时指定初始值。 例: 注意: 在进行结构变量的初始化时,数据与其对应的结构成员的类型要一一对应。 struct student { int num ; char name[20] ; char sex ; int age ; float score ; char addr[30] ; } stu={205,”Li Lin”,’M’,86.5,”123 Beijing Road”};
结构体类型变量的引用 在定义了结构体变量以后,当然可以引用这个变量,但应遵循以下规则: 不能将一个结构体变量作为一个整体进行输入和输出。例如已定义结构体变量stu且已赋初值:不能这样引用: printf(“%d,%s,%c,%d,%f,%s\n”,stu); 只能对结构体变量中的各个成员分别进行输入和输出,引用结构体变量中成员的方式为:结构体变量名.成员名 例如:stu.num表示stu变量中的num成员,即学号项。可以作为一个普通的同类型变量进行操作,如: stu.num=205; scanf(“%d”,&stu.num); stu.age++ 如果成员本身又是一个结构体类型,则要一级一级找到最低的一级成员,即只能对最低级的成员进行赋值或存取以及运算。
11.1.5 结 构 体 数 组 一个结构体变量中可以存放一组相关数据,如果要处理若干组相同结构的数据,显然应该用数组来实现。即是结构体数组。结构体数组存储的是一系列的结构体类型的数据。 定义结构体数组(数组的各元素在内存中连续存放) 和定义结构体变量的方法相似,只需说明其为数组即可。 1、struct student { int num ; char name[20] ; char sex ; int age ; float score ; char addr[30] ; } struct student stu[5]; 2 struct student { int num ; char name[20] ; char sex ; int age ; float score ; char addr[30] ; } stu[5]; 3 struct student { int num ; char name[20] ; char sex ; int age ; float score ; char addr[30] ; } stu[5];
11.结构体数组的初始化 1、struct student { int num ; char name[20] ; char sex ; int age ; float score ; char addr[30] ; } struct student stu[5]={{105,”Li Lin”,’M’,19,87.5,”103 Beijing Road”},{123,”Wang Min”,’F’,20,78,”101 Zhongshan Road”},{…},{…},{…}}; 2 struct student { int num ; char name[20] ; char sex ; int age ; float score ; char addr[30] ; } stu[]= {{105,”Li Lin”,’M’,19,87.5,”103 Beijing Road”},{123,”Wang Min”,’F’,20,78,”101 Zhongshan Road”},{…},{…},{…}};
结构体数组的应用举例 例1:设一个班有30名学生,读入学生的学号、姓名、性别、年龄、成绩等信息,并统计该班20岁以下同学的个数,算出该班的平均分。 main( ) {struct student {int num; char name[20]; char sex; int age; float score; }stu[30]; float sum, aver; int i, agenum; printf(“请依次输入学生的学号、姓名、性别、年龄、成绩。”); for (i=0;i<30;i++) scanf(“%d%s%c%d%f ”,&stu[i].num, stu[i].name, &stu[i].sex, &stu[I].age, &stu[i].score); agenum=0; sum=0; for(i=0;i<30;i++) { sum=sum+stu[i].score; if stu[i].age<20 agenum++; } aver=sum/30; printf(“The number of age<20 is :%d \n The average score is:%f\n”,agenum, aver); }
结构体数组的应用练习 练习1:设一个班有30名学生,读入学生的学号、姓名、性别、年龄、成绩等信息,并统计该班20岁以下男同学的个数。 main( ) {struct student {int num; char name[20]; char sex; int age; float score; }stu[30]; int i, agenum; printf(“请依次输入学生的学号、姓名、性别、年龄、成绩”); for (i=0;i<30;i++) scanf(“%d%s%c%d%f ”,&stu[i].num, stu[i].name, &stu[i].sex, &stu[i].age, &stu[i].score); agenum=0; for(i=0;i<30;i++) { if (stu[i].age<20 && stu[i].sex = = ‘F’) agenum++; } printf(“The number of age<20 is :%d ”,agenum,); }
指向结构体类型数组的指针 结构体类型指针的定义 Struct 结构体类型名 *结构体指针变量 如: P可以指向一个student类型的结构体变量,也可以指向一个student类型的结构体数组或student类型结构体数组中的元素。 struct student { int num ; char name[20] ; char sex ; int age ; float score ; } struct student *p, stu[10], stu1; 如:p=&stu1; p=stu; p++; p=&stu[2]; p p
指向结构体类型数组的指针 如: p=&stu[1]; 取值20的方法: stu[1].age (*p).age P - > age P->age++; (20) ++p->age; (21) 应用:用指针实现练习1: 指向运算符 stu[5] p
指向结构体类型数据的指针 判断下面的语句是否正确(假定struct student 已定义) struct student st[8],*p=st; int *q=&st[5].num; p=&st[5]; p++; 参见程序D11_7.c:有一个结构体变量stu,内含学生姓名,学号,三门课成绩。要求在主函数种赋值,在另一个函数print中将数据打印出来。
根据D11_7.c回答问题: 1、结构体类型的定义可否放在主函数之内,为什么? 2、假定子函数中把stu的有关成员改变了值是否影响主函数中的stu的成员? 3、可否把stu做成全局变量而不用传递参数? 4、主函数中的strcpy(stu.name,“Zhang Mary”);可否换成stu.name=“Zhang Mary”,为什么? 3、用结构体变量的指针做参数 把11_7.c改为指针实现。见程序D11_8.c 针对D11_8.c重新回答上面的4个问题,哪些答案发生了变化?为什么?这和前面的知识矛盾吗?
指向结构体数组的指针应用 例2:设一个班有30名学生,读入学生的学号、姓名、性别、年龄、成绩等信息,并统计该班20岁以下男同学的个数。 main( ) {struct student {int num; char name[20]; char sex; int age; float score; }stu[30],*p; int i, agenum; printf(“请依次输入学生的学号、姓名、性别、年龄、成绩。”); for (p=stu;p<stu+30;p++) scanf(“%d%s%c%d%f ”,&p->num, p->name, &p->sex, &p->age, &p->score); agenum=0; for(p=stu;p<stu+30;p++) { sum=sum+p->score; if p->age<20 agenum++; } printf(“The number of age<20 is :%d \n”,agenum); }
结构体变量与结构体数组练习 作业 1:定义一个结构体变量,包括年、月、日,计算该天在本年中是第几天?注意闰年问题。 作业2:输入两个复数,计算并输出他们的和及乘积,要求利用结构变量,返回结构数据类型的函数。 作业3:设有5名学生,读入学生的学号、性别、及3门课的成绩,输出这五个学生的信息并找出成绩最高的学生的学号。 作业4:利用结构体数组编程完成下列工作 假定有三个候选人:Zhangsan,Lisi,Wangwu,参加选举的人总共有20人,编程统计这三个人各得票多少张。
链 表 链表是一种常见的重要的数据结构。它是动态地存储分配的一种结构。 用数组存放数据时,必须事先定义固定的长度,如果事先难以确定数据的个数,则必须 把数组做的足够大,显然会造成内存的浪费。链表可以弥补这一缺点,即链表可以根据需要开辟存储空间。 如: 1249 1356 1475 1021 head 1249 说明: 1、有一个头指针,可由指针变量实现; 2、表尾指针; 3、中间部分每个结点由两部分组成 由结构体类型变量实现
链 表 head 1249 定义结构体类型变量实现链表的结点: struct student {float score; char name[10]; struct student *next; }; struct student *head;
链表的常用函数 分配存储区函数:malloc( ) Malloc(size); 从内存中分配一个长度为size个字节的连续存储区。 释放存储区函数:free( ) Free(*ptr); 释放由指针ptr所指向的存储区。 分配n个存储区函数:calloc( ) Calloc(n,size); 分配n个数据项的连续存储区,每个数据项大小为size个字节。 改变存储区大小函数:realloc( ) Reslloc(*p,size); 将指针p所指向的存储区的大小改为size个字节。 以上函数的原型在头文件stdlib.h中,使用前应做编译预处理。 (#include <stdlib.h>)
建 立 链 表(结点添加法) 1、定义链表结点的结构类型,并测试该结构类型所占内存的字节数。 2、说明链表指针head、p、h。其中head为头指针,p为创建新结点的指针,h为指向链表尾结点的指针 3、创建第一个结点,将该结点的首地址分别附给头指针head和尾指针p。 4、循环创建第n(1<n<=26)个结点,让h指向尾结点,让p指向新创建的结点。 5、创建结束,令尾结点的指针为NULL。
链表的建立 …… head 1、struct link {char ch; struct link *next; }; len=sizeof(struct link);
链表的建立 h p head 1、struct link {char ch; struct link *next; }; len=sizeof(struct link); 2、struct link *head, *p, *h;
链表的建立 …… head 1、struct link {char ch; struct link *next; }; len=sizeof(struct link); p h A head 2、struct link *head, *p, *h; 3、p=(struct link *)malloc(len); scanf(“%c”, p->ch); h=p; head=p;
链表的建立 …… head p 1、struct link {char ch; struct link *next; }; len=sizeof(struct link); p h h B A head 4、p=(struct link *)malloc(len); scanf(“%c”, p->ch); h->next=p; h=p; 2、struct link *head, *p, *h; 3、p=(struct link *)malloc(len); scanf(“%c”, p->ch); h=p; head=p;
链表的建立 …… head p 1、struct link {char ch; struct link *next; }; len=sizeof(struct link); h B A … head NULL 4、p=(struct link *)malloc(len); scanf(“%c”, p->ch); h->next=p; h=p; 2、struct link *head, *p, *h; 3、p=(struct link *)malloc(len); scanf(“%c”, p->ch); h=p; head=p; 5、h->next = NULL ;
链表的建立 …… head #include<stdlib.h> Main( ) {struct link {char ch; struct link *next; }; len=sizeof(struct link); struct link *head, *p, *h; int I=‘A’ ; p=(struct link *)malloc(len); scanf(“%c”, p->ch); h=p; head=p; while (I!=‘z’) {p=(struct link *)malloc(len); scanf(“%c”, p->ch); h->next=p; h=p; I++;} h->next = NULL ; }
链表的输出 p …… head 1、必须要知道链表的第一个结点的地址;(head) 2、另设一个指针变量p,令其指向head用来移动指针; 3、若该链表不为空,则输出p->ch; 4、移动指针:令p指向p->next; 5、重复以上操作,直至p为NULL(输出结束)。
链表的输出 p p p p …… head {struct link {char ch; struct link *next; }; Void print (struct link *head) {struct link *p; p = head; while (p!=NULL) {printf(“%c “, p->ch); p=p->next;} } …… Y Z A B C
链表的建立和输出练习 练习3:设有10名学生,读入学生的学号、性别、及3门课的成绩等信息,并输出该班学生信息。(用链表实现)
链表的删除 head 如:若要删除值为4的结点,只要让值为3的结点指向值为5的结点,然后释放值为4的结点的空间即可。 即:p->next=q->next; free(q); q p head
链表的删除 例:设有一个链表:每个结点包括工号、工资(如下所示)。写一函数del,删除工号为4的结点(设工号互不重复)。 Struct worker {int num; int money; struct worker *next; } While (p!=NULL && p->num!=4) {q=p; p=p->next;} if(p->=4) if (p==head) head=p->next; else {q->next=p->next; free(p); } else printf(“没找到”); Return(head); } Struct shuju *del(struct worker *head) { Struct worker *p,*q; If (head==null) goto end; P=head;
链表的删除练习 设有一个链表:每个结点的值为一个整数(如下所示)。写一函数del,删除值为4的倍数的结点。 Struct sj {int val; struct sj *next; } While (p!=NULL) {if (p->val%4!=0) {q=p; p=q->next;} else if (p==head) {head=p->next; p=head;} else {q->next=p->next; free(p); p=q->next;} } Return(head); } Struct sj *del(struct sj *head) {Struct sj *p,*q; Int I=0; If (head==null) goto end; P=head; p head
链表的插入 p q head Struct link {int val; struct link *next; }; len=sizeof(struct link); 如:若要在值为3的结点和值为7的结点中间插入一值为5的结点,需要: 1、开辟一个空间,存入值为5的结点;h=(struct link *)malloc(len); 2、让值为3的结点指向值为5的结点;p->next=h; 3、让值为5的结点指向值为7的结点。h->next=q;
链表的插入 p head Struct link {int val; struct link *next; }; len=sizeof(struct link); 如:若要在链表的最前端插入一值为5的结点,需要: 1、开辟一个空间,存入值为5的结点;h=(struct link *)malloc(len); 2、令指针p也指向链头,让head指针指向欲插入结点;p=head; head=h; 3、让值为5的结点指向值为7的结点。h->next=p;
链表的插入 p head Struct link {int val; struct link *next; }; len=sizeof(struct link); NULL 如:若要在链尾插入一值为5的结点,需要: 1、开辟一个空间,存入值为5的结点;h=(struct link *)malloc(len); 2、令指针p也指向链尾,让p指向欲插入结点;p->next = h; 3、让新插入的结点的next为NULL。h->next=NULL;
链表的操作练习 练习1:已有a、b两个链表,且它们的头指针分别为head1、head2。每个链表中的结点包括学号、成绩(如下所示)。要求把两个链表按学号的升序合并。(设学号互不重复) Struct student { int num; float score; struct student *next; } 练习2:将一个链表按逆序排列,即链头当链尾,链尾当链头。
p ^ b ^ s 双向链表 p->next head ^ a1 a2 a3 a4 ^ 设结点结构如下: struct node {int val; struct node *next; struct node *pre;} 设要在结点p的后面插入一个值为b的结点s,试写出指针的改变情况。 s->next=p->next; p->next->pre=s; p->next=s; s->pre=p;
p 双向链表 p->pre p->next head ^ a1 a2 a3 a4 ^ 设结点结构如下: struct node {int val; struct node *next; struct node *pre;} 设要删除结点p试写出指针的改变情况。 p->pre->next=p->next; p->next->pre=p->pre; free(p);
循环链表 head a1 a2 a3 循环链表有如下几个特点: 1、在循环链表中增加一个头结点,使得循环链表对空表和非空表的操作实现了统一; 2、循环链表最后一个结点的指针域不是空,而是指向头结点。 3、判断循环链表是否为空的办法是看看头结点的后继结点是否还是头结点。 4、在循环链表中,从任何一个结点出发,都可以访问到表中其他所有结点。
共用体类型的定义 在程序中,如需要存储一组性质相关、而类型不同的数据时,就可以使用结构体类型。 结构类型定义的一般形式: struct 结构类型名 { 成员类型1 成员名1; 成员类型2 成员名2; …… 成员类型n 成员名n; }; • 说明: • struct是结构类型定义的保留字,必须原样照写; • 结构类型名应符合标识符的规定,在以后的结构变量声明中可以被引用; • 大括号内是由结构成员组成的结构体,每个成员的数据类型可以是基本类型,也可以是构造类型。 • 不要漏掉分号。
共用体变量的引用 引用方法: 共用体变量名.成员名 [使用说明]: 1、共用体变量 不能进行初始化 2、一个共用体变量占用的内存空间,取决于共用体成员中占用内存空间最大的成员。 3、同一个共用体变量可以存储不同类型的成员,但每一时刻只能有一个成员起作用,其他成员不起作用。 4、共用体变量中起作用的成员是最后一次赋值的成员。在存储一个新的成员后,原有成员将失去作用。
共用体变量的类型特点 1、同一时刻只有一个成员有意义,其它无意义 如果有x.a=65;此时只有x.a有意义,但x.ch与x.f也有一个值,但没有任何实际意义。若再执行x.f=90.78;则此时只有x.f有意义,但x.ch与x.a也有一个值,但无任何实际意义。 union da {int a; char ch; float f;}x; 2、共用体变量的地址和其成员的地址是相同的 &x,&x.a,&x.ch,&x.f的值是一样的。 3、共用体变量在定义时不能赋初值 如:union da x={24,’A’,56.78};是错误的
共用体变量的类型特点 4、共用体变量不能做函数参数,函数返回值也不能是共用体类型。但其指针可做函数参数和返回值,其成员也可做函数参数和返回值
共用体类型变量的应用 • 参见课本11.8.2 • 例题11.12
枚举类型 和结构体以及共用体类型一样,枚举类型也是用户自己根据需要创建的一种新的数据类型。 如果一个变量,它只有有限的几种可能的取值,则这类变量就可以定义为枚举类型——因为这类变量的可能取值可以一一列举出来。 例:判断下列哪种变量可以定义为枚举类型的变量? 1、表示性别的变量 2、表示年份的变量 3、表示月份的变量 4、表示星期几的变量
一、 枚举类型的定义格式 enum 枚举类型名 {值1,值2,值3,……值n}; 例如:命令enum sex{male,female};定义了一种新的数据类型enum sex,这种类型的变量的值只有两种可能,要么是male,要么是female。 例如:enum weekday{sun,mon,tue,wed,thu,fri,sat};命令定义了另一种新的数据类型enum weekday,这种类型的变量的值只能是sun,mon……sat之一。
一、 枚举类型的定义格式 例:判断下面语句的对错 1、enum sex a,b,c; 2、enum weekday workday; 3、enum color{red,black,green}c1,c2; 上面的三条语句都对。1和2是先定义类型,再定义变量,3是在定义类型的同时定义变量
二、 枚举元素和枚举常量 若用enum weekday{sun,mon,tue,wed,thu,fri,sat};命令定义了另一种新的数据类型enum weekday,则{}里面的sun,mon……sat等被称为枚举常量或枚举元素。 使用它们必须注意以下事项: 1、默认情况下,枚举元素按照它们所在的位置,它们所代表的值分别是0,1,2,3…… 例如:若有enum weekday x; x=tue;则定义x为enum weekday类型的变量,且把x的值设为tue,则x实质是得到了一个常量2。