1、第8章 结构体、共用体与枚举类型,本章要求: 理解结构体类型的概念,掌握结构体变量的定义和使用掌握结构体数组、结构体指针的定义和使用理解共用体的概念,掌握共用体变量的定义和使用了解枚举数据类型及自定义数据类型的使用理解链表的概念,初步掌握动态链表的常见操作 本章重点: 结构体变量的定义和使用 链表的概念 本章难点:动态链表的常见操作,8.1 结构体类型与结构体变量,8.1.1 结构体概述 数组是一种简单构造类型数据,数组中的各元素是属于同一个类型的。 但在实际问题中,常会遇到这样一类数据,它由多个类型不相同的数据项组成。 学生基本情况: 学号、姓名、性别、年龄、成绩 结构体(structure
2、)是一种数据类型。C语言本身没有提供具体的结构体类型,但提供了说明结构体类型的方法。,8.1.2 结构体的声明,声明结构体类型的一般形式:struct 结构体名类型标识符 成员名;类型标识符 成员名;.;,成员类型可以是 基本型或构造型,struct关键字, 不能省略,合法标识符 可省:无名结构体,struct student int num;char name20;char sex;int age; ;,8.1.3 结构体变量的定义,定义好一个结构体类型后,我们可以将其看作是和int、char、float等数据类型一样的一个新的数据类型。1.先定义结构体类型,再定义结构体变量,struct
3、结构体名 类型标识符 成员名;类型标识符 成员名;. ; struct 结构体名 变量名表列;,struct student int num;char name20;char sex;int age; ; struct student a,b;,int x;,a,struct student t;,可多次使用struct student 来定义变量,2. 定义结构体类型的同时定义结构体变量,struct 结构体名 类型标识符 成员名;类型标识符 成员名;. 变量名表列;,struct student int num;char name20;char sex;int age; a,b;,3. 直
4、接定义结构体变量,struct student t;,用无名结构体直接定义变量只能一次,8.1.4 结构体变量的引用格式,引用规则: 结构体变量不能整体引用,只能引用变量成员 引用方式: 结构体变量名. 成员名,struct student int num;char name20;char sex;int age;float score;char addr30; stu1,stu2;,stu1.num=10; stu1.score=85.5; stu1.score+=stu2.score; stu1.age+;,printf(“%d,%s,%c,%d,%f,%sn”,stu1); stu1=1
5、01,“Wan Lin”,M, 19,87.5,“DaLian”;,结构体嵌套,struct date int month;int day;int year; ;,struct person char name20;char sex;struct date birthday; ;,若有定义:struct person person1;,合法:person1.sex=F; person1.birthday.day=35;,同类型结构体变量间的整体赋值,及变量的初始化,struct student int num;char name20;char sex;int age;float score;c
6、har addr30; stu1,stu2 ;,stu1=101,“Wan Lin”,M, 19,87.5,“DaLian”;,stu1=stu2;,初始化: struct student stua=101, “Wan Lin”,M,19,87.5, “DaLian”;,=111,“Tom”,M,19,60, “USA”;,8.1.6 指向结构体的指针,指向结构体变量的指针的值是该结构体变量所分配的存储区域的首地址。 结构指针变量的定义,struct student int num;char name20;char sex;int age; stu; struct student *p=,st
7、u,通过指针访问结构体变量的成员, 结构变量名.成员名(名字引用); 结构指针-成员名(指针引用); (*结构指针).成员名(将指针转化为名字引用);,struct student int num;char name20;char sex;int age; stu; struct student *p=,stu.age=8; p-age=8; (*p).age=8;,stu,8.2 结构体数组,8.2.1结构体数组的定义(三种形式),struct student int num;char name20;char sex;int age; ; struct student stu2;,stu2;
8、,8.2.2 结构体数组的初始化,例如:struct student int num;char name20;char sex;int age; struct student stu =100,“He Lin”,M,20,101,“Li Gang”,M,19, 110,“Liu Yan”,F,19;,引用方式: 结构体数组名下标.成员名,stu0.age=8; printf(“%s”,stu1.name);,8.2.3 指向结构体数组的指针,struct student int num;char name20;char sex;int age; ; struct student stu9; s
9、truct student *p=stu;,改写数组输出例子,8.3 结构体与函数,已知:struct student stu1,stu10,*p=stu; 用结构体变量的成员作参数-值传递 函数声明:void func(int x); 函数调用:func(stu1.age);用指向结构体指针变量或数组的指针作参数-地址传递 或 st 函数声明:void func(struct student *st ); 函数调用:func(stu); 或 func(p);用结构体变量作参数-多值传递(效率低) 函数声明:void func(struct student z); 函数调用:func(stu1
10、);,编写: 已知5个学生的信息表,包括学号、姓名、性别、年龄。 编写函数find1,找制定学号的学生姓名。 在主函数中输入学生学号,调用find1函数查找学生信息。 编写函数find2,找制定姓名的学生信息。 在主函数中输入学生姓名,调用find2函数查找学生信息。,用一个结构体的指针作为 find2函数的返回值,8.4 共用体,共用体(也叫联合体)是构造数据类型,使几个不同类型的变量共占一段内存(相互覆盖) 1.声明结构体类型的一般形式:union 共用体名类型标识符 成员名;类型标识符 成员名; .;,union data int i;char ch;float f; ;,union d
11、ata int i;char ch;float f; ; union data a;,a,1B,2B,4B,i,ch,f,共用体变量任何时刻-只有一个成员存在 共用体变量定义分配内存,长度=最长成员所占字节数 共用体变量的定义形式有三种(类似结构体变量的定义),2.共用体变量引用(类似结构体变量),union data int i;char ch;float f; a,b,d5,*p=d;,a.i a.ch a.f p-i p-ch p-f (*p).i (*p).ch (*p).f d0.i d0.ch d0.f,结构体 与 共用体,区别: 存储方式不同 联系: 两者可相互嵌套,变量的各成员
12、同时存在,任一时刻只有一个成员存在,例,8.5 枚举类型,枚举类型就是将变量可能出现的值放在一起而形成的一个整型常量的集合类型。限制在此集合内,变量只能取这个集合中的某个值。 1枚举类型的定义: enum 枚举类型名 取值表; 例 enum weekdays Sun,Mon,Tue,Wed,Thu,Fri,Sat;2枚举变量的定义与结构变量类似 (1)间接定义 例如,enum weekdays workday; (2)直接定义 例如,enum Sun,Mon,Tue,Wed,Thu,Fri,Sat workday;,3说明,(1)枚举型仅适应于取值有限的数据。 (2)取值表中的值称为枚举元素,
13、枚举元素是常量。在编译器中,按定义的顺序取值0、1、2、.。所以枚举元素可以进行比较,比较规则是:序号大者为大。例如,上例中的Sun=0、Mon=1、Sat=6,所以MonSun、Sat最大。 (3)枚举元素的值也是可以人为改变的:定义时由程序指定。例如,如果enum weekdays Sun=7, Mon=1 ,Tue, Wed, Thu, Fri, Sat; 则Sun=,Mon=,从Tue=2开始,依次增。,例,8.6 typedef的使用,功能:用自定义名字为已有数据类型命名 简单形式: typedef type newname;,类型定义语句关键字,已有数据类型名,用户定义的类型名,例
14、 typedef int INTEGER;typedef float REAL;,typedef 没有创造新数据类型。 typedef是编译时处理为已有类型命名, #define是预编译时处理时简单字符置换。,typedef定义类型步骤:,按定义变量方法先写出定义体,如 int i; 将变量名换成新类型名,如 int INTEGER; 他最前面加typedef,如 typedef int INTEGER; 用新类型名定义变量,如 INTEGER i,j;,例:,int a100; int ARRAY100;,typedef int ARRAY100;,ARRAY a,8.7 动态数据结构链表,
15、8.7.1 链表的提出 数组: 在内存中占用连续存储的空间。 插入,删除操作需要移动多个元素。链表 是动态的进行存储分配,链表的各个结点在逻辑上是连续的,但是在内存中存储时不占用连续的空间。链表的使用能有效的避免存储空间的浪费和数据移动的问题。,8.7.2 链表的基本结构,链表是一种常用的、能够实现动态存储分配的数据结构。 头指针变量head指向链表的首结点。 每个结点一般由2个域组成: 数据域存储结点本身的信息。 指针域指向后继结点的指针。 尾结点的指针域置为“NULL(空)”,作为链表结束标志,8.7.3 链表结点的定义,struct student int num;float score
16、;struct student *next; ;,一个结点,单向链表,8.7.4 单向链表的访问,1. 输出链表各个结点的数据,void print_link(struct student *head) struct student *p; p=head;while(p!=NULL) printf(“%d,%6.1fn”,p-num,p-score);p=p-next; ,2. 统计链表的长度,void len_link(struct student *head) int n=0; struct student *p=head;while(p!=NULL) n+; p=p-next; retu
17、rn(n); ,8.7.5 动态存储空间的建立和释放,1动态存储空间的建立 malloc函数,其函数原型为: void *malloc(unsigned int size); 其作用是在内存的动态存储区中分配一个长度为size的连续空间。 sizeof(type)运算符 计算所给数据类型type的字节数,主要用来计算链表中结点所占动态存储空间的字节数。,p=(struct student * ) malloc (sizeof (struct student) );,calloc函数,其函数原型为: void *calloc(unsigned n,unsigned size); 其作用是在内存的
18、动态区存储中分配n个长度为size的连续空间。 2. 动态存储空间的释放free函数,其函数原型为: void free(void *p);其作用是释放由p指向的内存区,使这部分内存区能被其他变量使用。p是调用calloc或malloc函数时返回的值。,p=(struct student * ) malloc (sizeof (struct student) );,free(p);,8.7.6 动态链表的建立,建立链表主要步骤: 先设三个指针变量:head、p1、p2,它们都是用来指向struct student类型数据的。struct student *head=NULL,*p1,*p2;
19、head:头指针变量,指向链表第一个结点,作函数返回值。 p1:指向新申请的结点。 p2:指向链表的尾结点, 在将新结点连接到链表末 尾时需要。,结点,2. malloc函数开辟第一个结点,并使head和p2都指向它。head=p2=(struct student * )malloc (sizeof(struct student); scanf(“%d%f“,3. 再用malloc函数开辟另一个结点并使p1指向它,接着输入该结点的数据,并与上一结点相连,且使p2指向新建立的结点。 建立新结点: p1= ( struct student*) malloc (sizeof(struct stude
20、nt); scanf(“%d%f“,p1,使新结点与上一结点连接: p2-next=p1; 使p2指向新链结点:p2=p1;,p2,p2,重复第3步的,建立更多结点: p1= ( struct student*) malloc (sizeof(struct student); scanf(“%d%f“,head,1002,87,p1,p2,p1,p1,p2,p2,4. 给末结点的指针域赋值NULL p2-next=NULL;,NULL,struct student *create(int n) int i; struct student *head=NULL,*p1,*p2;head=p2=(
21、struct student * ) malloc(LEN);scanf(“%d%f“, ,#define LEN sizeof(struct student),1.,2.,3.,4.,8.7.7 链表的删除操作,删除首结点 使p1指向第一个结点,用以下语句实现删除首结点操作。 p1=head; head=p1-next; free(p1);,p1,删除其它结点 删除链表的中间结点通过将下一结点地址赋给前一结点地址来实现,即将要删除的p1结点的后继地址,放入前一结点p2的地址域,同时释放p1结点。 p2-next=p1-next; free(p1);,head,8.7.8 链表的插入操作,先设
22、四个指针变量: head:要插入链表的头指针。 p0:指向要插入的结点。 p1:指向当前结点。 p2:指向前一结点。,四种情况:,原链表为空表,即head=NULL时, 用以下语句实现插入操作: head=p0; p0-next=NULL;,NULL,head,NULL,无p1、p2,在头结点之前插入,若p1=head,表示插入结点在头结点之前。用以下语句实现插入操作: head=p0; p0-next=p1,head,p0,p1,无p2,在中间插入,表示在在非头结点之前,非尾节点之后插入结点,用以下语句实现插入操作: p2-next=p0; p0-next=p1;,head,p0,尾节点之后插入,用以下语句实现插入操作: p2-next=p0; p0-next=NULL;,head,p0,p1为NULL 可理解为无p1,NULL,