《结构体、共用体与枚举类型.ppt》由会员分享,可在线阅读,更多相关《结构体、共用体与枚举类型.ppt(54页珍藏版)》请在三一办公上搜索。
1、第8章 结构体、共用体与枚举类型,本章要求:,理解结构体类型的概念,掌握结构体变量的定义和使用;掌握结构体数组、结构体指针的定义和使用;理解链表的概念,掌握动态链表的常见操作;理解共用体的概念,掌握共用体变量的定义和使用。,本章重点:动态链表的常见操作,本章难点:动态链表的常见操作,第8章 结构体、共用体与枚举类型,8.1 结构体类型与结构体变量 8.2 结构体数组 8.3 结构体与函数 8.4 动态数据结构链表8.5 共用体8.6 枚举类型8.7 自定义类型标识符(typedef),第8章 结构体、共用体与枚举类型,8.1 结构体 C语言提供一种能集中不同数据类型于一体的数据类型结构体类型。
2、结构体类型的变量可以拥有不同数据类型的成员,是不同数据类型成员的集合。结构体是一种构造数据类型 用途:把不同类型的数据组合成一个整体自定义数据类型,8.1 结构体类型与结构体变量,8.1.1 什么是结构体类型,第4章学习的数组是一种简单构造类型数据,数组中的各元素是属于同一个类型的。但在实际处理的问题中,常会遇到这样一类数据,它由多个属性各不相同的数据项组成,各个数据项用来描述一个共同的对象。结构体(structure)是一种数据类型。C语言本身没有提供具体的结构类型,但提供了说明结构体类型的方法。,8.1.2 结构体的声明,声明结构体类型的一般形式:,struct 结构体名 结构成员1;结构
3、成员2;结构成员n;,8.1 结构体类型与结构体变量,例如:定义一个可描述学生基本情况的结构体类型如下:struct student int num;char name20;char sex;int age;float score;,8.1 结构体类型与结构体变量,例 struct student int num;char name20;char sex;int age;float score;struct student stu1,stu2;,8.1.3 结构体变量的定义 定义好一个结构体类型后(也可以理解为是我们用户自己定义的类型),我们可以将其看作是和int、char、float等数据类
4、型一样的一个新的数据类型,可以用它定义变量,通过变量对结构体中各成员的数据进行处理。1.先定义结构体类型,再定义结构体变量一般形式:,struct 结构体名 类型标识符 成员名;类型标识符 成员名;.;struct 结构体名 变量名表列;,8.1 结构体类型与结构体变量,2.定义结构体类型的同时定义结构体变量一般形式:,struct 结构体名 类型标识符 成员名;类型标识符 成员名;.变量名表列;,例 struct student int num;char name20;char sex;int age;float score;stu1,stu2;,8.1.3 结构体变量的定义,3.直接定义结
5、构体变量一般形式:,struct 类型标识符 成员名;类型标识符 成员名;.变量名表列;,例 struct int num;char name20;char sex;int age;float score;stu1,stu2;,8.1.3 结构体变量的定义,8.1.3 结构体变量的引用,1结构体变量的引用格式 结构体变量名.成员名其中:“.”是成员运算符,它在所有的运算符中优先级最高。例如,对上小节定义的学生情况结构体变量stu1变量进行赋值:stu1.num=10001;strcpy(stu1.name,”zhang”);stu1.sex=M;stu1.age=19;stu1.score=8
6、8;不能把结构变量作为整体进行输入输出:scanf(“%d%s%c%d%f”,stu1);正确的引用方式可以是:gets(stu1.name);scanf(“%d%c%d%f”,2结构体嵌套时逐级引用,struct date int month;int day;int year;,struct person char name20;char sex;struct date birthday;,若有定义:struct person person1;这时可以这样访问person1中的成员month:,8.1.3 结构体变量的引用,8.1.3 结构体变量的引用,33.同类型结构体变量间的整体赋值,结
7、构体变量可以通过整体赋值,将一个结构体变量中的所有数据,赋给另一个结构体类型相同的结构体变量中对应的数据成员。,例8-1 建立一个学生的基本情况表,然后将其打印输出。程序如下:,#include#includemain()struct student int num;char name20;char sex;int age;float score;stu1,stu2;stu1.num=10001;strcpy(stu1.name,zhang);stu1.sex=M;stu1.age=19;stu1.score=88;stu2=stu1;printf(stu1:%d,%s,%c,%d,%6.2f
8、n,stu1.num,stu1.name,stu1.sex,stu1.age,stu1.score);printf(stu2:%d,%s,%c,%d,%6.2fn,stu2.num,stu2.name,stu2.sex,stu2.age,stu2.score);,程序输出结果如下:stu1:10001,zhang,M,19,88.00stu2:10001,zhang,M,19,88.00,struct 结构体名 类型标识符 成员名;类型标识符 成员名;.结构体变量=初始数据;,例 struct student int num;char name20;char sex;int age;float
9、 score;stu1=10001,zhang,M,19,88;,8.1.4 结构体变量的初始化,形式:,8.1.6 指向结构体的指针,指向结构体变量的指针的值是该结构体变量所分配的存储区域的首地址。,结构指针变量的定义,例:struct student int num;char name20;char sex;int age;float score;stu1=10001,”zhang”,M,19,88;struct student*p=stu1;,8.1.6 指向结构体的指针,通过指针访问结构体变量的成员,结构变量中简单成员的引用形式有下面三种:结构变量名.成员名(名字引用);结构指针-成员
10、名(指针引用);(*结构指针).成员名(将指针转化为名字引用);,例:若有定义:struct student*p,stu1;且p=则要访问stu1的成员(如age)有下三种方式:stu1.age 或 p-age 或(*p).age,8.2 结构体数组,8.1.1结构体数组的定义,结构体数组的定义方式同结构体变量一样,也有3种方法,只需把结构体变量换成结构体数组即可。,例:struct student int num;char name20;char sex;int age;float score;struct student stu10;,结构体数组的初始化,与通常数组的初始化类似。例如:st
11、ruct student int num;char name20;char sex;int age;float score;stu4=10001,Zhang,M,19,80,10002,Li,M,18,78,10003,He,F,20,92.5,10004,Cheng,F,18,70;,结构体数组的初始化,8.2 结构体数组,结构体数组引用方式:结构体数组名下标.成员名 例:stu1.age+,8.2.2 结构体数组与指针,指向结构体数组的指针的值是该结构体数组所分配的存储区域的首地址。,例:struct student int num;char name20;char sex;int age
12、;float score;struct student*p;struct student stu10;p=stu;,p就是指向结构体数组stu的指针(如右图所示),结构体数组的应用实例,例8-3,#define N 10#include#includestruct student/*定义student结构体类型*/int num;char name20;float score3;main()int i,j;struct student stuN;for(i=0;iN;i+)/*用for循环输入10名学生的信息*/scanf(%d,%s,例8-4:用指向结构体数组的指针改写例8-3,#defin
13、e N 10#include#includestruct student int num;char name20;float score3;main()int i,j;struct student*p,stuN;p=stu;for(i=0;inum);printf(%s,p-name);printf(%6.1f,%6.1f,%6.1fn,p-score0,p-score1,p-score2);,8.3 结构体与函数,结构体类型的变量可以作函数的形参,调用时C直接把同类型结构体实参变量的各个数据成员的值全部复制给形参的结构体变量。为了提高效率和通用性,可以用指向结构体变量(或数组)的指针作为函数
14、的形参。函数也可以返回结构体类型数据到主调函数中。,8.3.1 结构体变量作函数参数,例8-5 在例8-3中,10名学生的信息输出改为通过调用print函数完成。,void print(struct student stu)printf(%d,%s,%6.1f,%6.1f,%6.1fn,stu.num,stu.name,stu.score0,stu.score1,stu.score2);,#define N 10#include#includestruct student;main()int i,j;struct student stuN;void print(struct student s
15、tu);for(i=0;iN;i+)print(stui);/调用print函数,8.3.2 返回结构体的函数,例8-6 建立10名学生的信息表,每个学生的数据包括学号、姓名、及三门课的成绩。输出总分最高的学生记录,要求将查找该记录的过程编制为函数。,/查找最高分学生的记录函数,返回值为指向该记录的指针 student*search_max(student*x,int n)int i,k=0;for(i=1;ixk.sum)k=i;return x+k;,8.4 动态数据结构链表,8.4.2 链表的基本结构,1 头指针(head)是一个指针变量,用来存放链表中第一个结点的地址。2 链表中每一结
16、点一般由两大部分组成:(1)数据域,用于存放用户需要用的实际数据,可以是一个数据项,也可以是多个数据项。(2)指针域,用于存放和该结点相链接的下一个结点的地址,一般通过一个指针变量来实现。3尾结点 最后一个结点因其后续无结点,该指针域不再指向其他结点,它称为尾结点,它的地址部分存放一个“NULL”(表示空地址),链表到此结束。,8.4 动态数据结构链表,8.4.3 链表结点的定义,链表结点数据可以用结构体来描述,如图8-4所示中的链表结点就可通过结构体定义如下:struct student int num;float score;struct student*next;以上定义了一个结构体st
17、udent类型,student类型数据包括3个数据成员:int类型num、float类型的score和指向另一个student类型数据的指针变量next。,8.4.4 动态存储空间的建立和释放,1动态存储空间的建立(1)malloc函数,其函数原型为:void*malloc(unsigned int size);其作用是在内存的动态存储区中分配一个长度为size的连续空间。,(2)sizeof(type)运算符计算所给数据类型type的字节数,主要用来计算链表中结点所占动态存储空间的字节数。,(3)calloc函数,其函数原型为:void*calloc(unsigned n,unsigned
18、size);其作用是在内存的动态区存储中分配n个长度为size的连续空间。,8.4.4 动态存储空间的建立和释放,2.动态存储空间的释放free函数,其函数原型为:void free(void*p);其作用是释放由p指向的内存区,使这部分内存区能被其他变量使用。p是调用calloc或malloc函数时返回的值。,链表的基本操作包括建立链表、输出链表、查找链表中某个结点、在链表中插入一个新的结点、删除链表中的某个结点。,8.4.5 动态链表的建立,建立链表的主要步骤:(链表结点为struct student类型数据结构)(1)先设三个指针变量:head、p1、p2,它们都是用来指向struct
19、student类型数据的。struct student*head=NULL,*p1,*p2;head:头指针变量,指向链表的第一个结点,用作函数返回值。P1:指向新申请的结点。P2:指向链表的尾结点,用P2-next=P1,实现将新申请的结点插入到链表尾,使之成为新的尾结点。(2)malloc函数开辟第一个结点,并使head和p2都指向它。head=p2=(struct student*)malloc(sizeof(struct student);/*申请一个新结点的空间*/scanf(%d%f,/*读入数据*/,8.4.5 动态链表的建立,(3)再用malloc函数开辟另一个结点并使p1指向
20、它,接着输入该结点的数据,并与上一结点相连,且使p2指向新建立的结点。建立新结点:p1=(struct student*)malloc(sizeof(struct student);scanf(%d%f,如图8-6(c)所示:,8-6(a),8-6(b),8-6(c),重复执行第(3)步,可以建立第三个结点,并使第三个结点和第二个结点链接(p2-next=p1;),依次创建后面的结点,直到所有的结点建立完毕。(4)为末结点的指针域赋值NULL(p2-next=NULL;),8.4.5 动态链表的建立,例8-7 建立有n个student类型结点的链表,n的值从键盘输入,再输出链表。,#defin
21、e NULL 0#define LEN sizeof(struct student)#include#include/*定义结点*/struct student int num;float score;struct student*next;/*main函数中,分别调用create和print函数建立和输出链表*/void main()int n;struct student*head;struct student create(int n);/*函数声明*/void print(struct student*head);/*函数声明*/printf(please input nn);scan
22、f(%d,/*create(int n)函数:创建一个具有头结点的单链表*/*形参n值:创建链表的结点数*/*返回值:返回单链表的头指针*/struct student*create(int n)int i;struct student*head=NULL,*p1,*p2;head=p2=(struct student*)malloc(LEN);/*申请一个新结点的空间*/scanf(%d%f,例8-7 建立有n个student类型结点的链表,n的值从键盘输入,再输出链表。,/*void print(struct student*head)为链表输出函数*/*形参head为要输出链表的头指针*
23、/void print(struct student*head)struct student*p;p=head;while(p!=NULL)printf(%d,%6.1fn,p-num,p-score);p=p-next;/*移动指针*/,例8-7 建立有n个student类型结点的链表,n的值从键盘输入,再输出链表。,8.4.6 链表的删除操作,下面讨论在结点类型为student的链表中删除num域为某个值的结点的主要操作步骤(链表结点为struct student类型数据结构):设三个指针变量:head、p1、p2,它们都是用来指向struct student类型数据的,head指向链表头
24、,若head=NULL,则链表为空,没有可删除结点。查找要删除的结点,使p1指向它,p2指向p1的前一结点(前驱)。有两种情况:删除首结点和其它结点。,(1)删除首结点使p1指向第一个结点,用以下语句实现删除首结点操作。p1=head;head=p1-next;free(p1);,8.4.6 链表的删除操作,(2)删除其它结点 删除链表的中间结点通过将下一结点地址赋给前一结点地址来实现,也即将要删除的p1结点的后继地址,放入前一结点p2的地址域(p2-next=p1-next;),同时释放p1结点。,/*该函数的功能是从表头指针为head的链表中删除num域的值与形参num相等的结点,释放该结
25、点的存储空间,返回该链表的表头指针。*/,struct student*delete(struct student*head,int num)struct student*p1,*p2;if(head=NULL)printf(list null!n);else p1=head;while(p1!=NULL,8.4.6 链表的删除操作,8.4.7 链表的插入操作,下面讨论在结点类型为student的链表中插入一个新生结点的主要操作步骤(链表结点为struct student类型数据结构):先设四个指针变量head、p0、p1、p2:head:要插入链表的头指针。P0:指向要插入的结点。P1:指向
26、当前结点,首先使p1 指向头结点(p1=head)。P2:指向前一结点(即P1 原来指向的结点)。首先查找插入的位置,应插入的位置:p1之前,p2之后。在链表中,各结点按成员score(成绩)由小到大顺序存放,从第一个结点开始,把待插入结点p0-score与每一个结点p1-score比较,若(p0-score)(p1-score),则p1移到下一个结点(p1=p1-next),同时,p2也移到下一个位置(p2=p1)。,(1)原链表为空表 即head=NULL时,用以下语句实现插入操作:head=p0;p0-next=NULL;p0是该链表惟一一个结点。(2)在头结点之前插入 当p0-scor
27、escore 时,若p1=head,表示插入结点在头结点之前。用以下语句实现插入操作:head=p0;p0-next=p1;,8.4.7 链表的插入操作,插入前,插入后,8.4.7 链表的插入操作,(3)在非头结点之前,非尾节点之后插入 当p0-scorescore,且p1head时,表示在在非头结点之前,非尾节点之后(中间结点)插入结点,用以下语句实现插入操作:p2-next=p0;p0-next=p1;,插入前,插入后,8.4.7 链表的插入操作,(4)尾节点之后插入 当p0-scorep1-score 时,表示在尾节点之后插入结点,用以下语句实现插入操作:p1-next=p0;p0-ne
28、xt=NULL;,插入前,插入后,函数insert的功能是:在一个有序链表中插入一个结点。,在链表head中,插入stud结点,并返回新链表的表头struct student*insert(struct student*head,struct student*stud)struct student*p0,*p1,*p2;p1=head;p0=stud;if(head=NULL)/*原链表是空表*/head=p0;p0-next=NULL;else/*while循环查找待插入的位置*/while(p0-score p1-score),union 共用体名 类型标识符 成员名;类型标识符 成员名;
29、.;,例 union data int i;float f;char ch;,1.共用体类型定义定义形式:,构造数据类型,也叫联合体用途:使几个不同类型的变量共占一段内存(相互覆盖),8.5 共用体,形式一:union data int i;char ch;float f;a,b;,形式二:union data int i;char ch;float f;union data a,b,c;,形式三:union int i;char ch;float f;a,b,c;,2.共用体变量的定义,8.5 共用体,共用体变量定义分配内存长度=最长成员所占字节数,8.5 共用体,共用变量的引用与结构变量一
30、样,也只能逐个引用共用变量的成员。例如,访问共用变量un1各成员的格式为:un1.i、un1.ch、un1.f。,对共用体变量的使用需要注意如下几个问题:(1)系统采用覆盖技术,实现共用变量各成员的内存共享,所以在某一时刻,存放的和起作用的是最后一次存入的成员值。(2)由于所有成员共享同一内存空间,故共用变量与其各成员的地址相同。(3)不能对共用变量进行初始化(注意:结构变量可以);也不能将共用变量作为函数参数,以及使函数返回一个共用数据,但可以使用指向共用变量的指针。(4)共用类型可以出现在结构类型定义中,反之亦然。,例8-9,8.5 共用体,假设一个学生的信息表中包括学号、姓名和一门课的成
31、绩。而成绩通常又可采用两种表示方法:一种是五分制,采用的是整数形式;另一种是百分制,采用的是浮点数形式,现要求编一程序,输入一个学生的信息并显示出来。程序如下:#include struct stu_score int num;char name20;int type;/*type值为0时五分制,type值为1时百分制*/union mixed int iscore;/*五分制*/float fscore;/*百分制*/score;,8.5 共用体,main()struct stu_score stud1;printf(please input num,name,type:n);scanf(%
32、d%s%d,8.6 枚举类型,1枚举类型的定义 enum 枚举类型名 取值表;例如,enum weekdays Sun,Mon,Tue,Wed,Thu,Fri,Sat;声明了一个枚举类型enum weekdays,可以用此类型来定义变量。枚举变量的定义(与结构变量类似)(1)间接定义:先声明类型,再定义变量。例如,enum weekdays workday;(2)直接定义:声明类型类型的同时直接定义变量。例如,enum weekdays Sun,Mon,Tue,Wed,Thu,Fri,Sat workday;,说明(1)枚举型仅适应于取值有限的数据。例如,根据现行的历法规定,周天,年个月。(2
33、)取值表中的值称为枚举元素,枚举元素是常量。在编译器中,按定义的顺序取值0、1、2、.。所以枚举元素可以进行比较,比较规则是:序号大者为大!例如,上例中的Sun=0、Mon=1、Sat=6,所以MonSun、Sat最大。(3)枚举元素的值也是可以人为改变的:在定义时由程序指定。例如,如果enum weekdays Sun=,Mon,Tue,Wed,Thu,Fri,Sat;则Sun=,Mon=,从Tue=2开始,依次增。,8.6 枚举类型,例8-10,#include main()enum weekdays sun,mon,tue,wed,thu,fri,satdate;int i;printf
34、(please input the date(1-30):);scanf(%d,8.6 枚举类型,8.7 typedef的使用,功能:用自定义名字为已有数据类型命名类型定义简单形式:typedef type name;,例:typedef int INTEGER;typedef float REAL;定义新数据类型INTEGER,它代表已有数据类型int。定义新数据类型REAL,它代表已有数据类型float。,说明:(1)用typedef只是给已有类型增加个别名,并不能创造个新的类型。就如同人一样,除学名外,可以再取一个小名(或雅号),但并不能创造出另一个人来。(2)typedef与#define有相似之处,但二者是不同的:前者是由编译器在编译时处理的;后者是由编译预处理器在编译预处理时处理的,而且只能作简单的字符串替换。,8.7 typedef的使用,本章小结,本章详细讨论了结构体的概念、定义和使用方法,通过实例介绍了结构体数组与指针,以及用结构体数据作为函数参数和返回值的使用方法,还详细介绍了链表的概念、定义、特点、操作方式等。另外,本章还介绍了能够在相同存储区域中存储不同数据类型的另一种构造类型公用体以及有关枚举类型和用户定义类型的概念和应用。,本章作业,