《C语言课件第11章结构体与共用体.ppt》由会员分享,可在线阅读,更多相关《C语言课件第11章结构体与共用体.ppt(54页珍藏版)》请在三一办公上搜索。
1、第11章结构体与共用体,1、结构体类型的定义2、结构体变量的定义及引用3、结构体数组4、结构体类型的指针5、链表的基本操作6、共用体和枚举类型(了解),结构体类型的引入,问题:为了描述一个事物的不同属性,需要用到各种不同类型的数据,这些数据彼此相关,形成一个有机的整体。例如:一个教师的基本信息由姓名、性别、年龄、职称、工资等几项组合而成。如何描述一个教师的情况呢?前面我们已学习过各种基本类型的变量和数组,而且我们知道,各个变量之间是相互独立的,无任何联系;而数组只能用来表示一批相同类型的数据。因此,若用单个变量分别表示教师的姓名、性别、年龄等属性,则难以反映他们之间的内在联系;若用数组,则根本
2、无法表示,因为姓名、性别、年龄等不属同一种数据类型。C语言中用“结构体”来描述由多个不同类型的数据组成的数据集合。相当于其他高级语言中的“记录”.,11.1结构体类型的定义,与基本数据类型不同的是,结构体是又一种构造类型,是由多个类型的数据成员组合而来的。因此该类型的具体内容应根据需要先定义,后使用。可以定义如下结构体类型来描述教师的基本情况:struct teacher/*struct 是关键字*/char name30;/*内是该类型的各成员*/char sex;int age;char position10;float salary;/*语句末尾是“;”*/该结构体类型名为struct
3、teacher,teacher 是该结构体的标识符;该类型包含有6个成员的数据项:name、sex、age、position 和salary,其中每个成员项都有自己的类型。,可见,定义一种新的结构体类型的一般形式是:struct 结构体类型名 成员类型 成员名;成员类型 成员名;其中,struct 是 关键字,结构体类型名、结构体成员名的命名规则同变量的命名规则一样。特别提示:struct teacher只是一种具体的结构体类型,根据需要,程序员可以定义多个不同内容的结构体类型。其中的成员项是该类型的组成部分,而不是变量。结构体类型的成员可以是基本类型的变量或数组,也可以是结构体类型的数据。即
4、结构体类型的嵌套定义。例如,若将教师的年龄改为出生日期,则可以将出生日期定义为一个结构体类型,然后嵌入 struct teacher中。,将出生日期单独定义为一个结构体类型后再嵌入。Struct date_type int year;int month;int day;Struct teacher_2 char name30;char sex;struct date_type birthday;char position10;float salary;(常用),直接嵌入。Struct teacher_3 char name30;char sex;struct date_type int yea
5、r;int month;int day;birthday;char position10;float salary;/*成员birthday又是一个结构体类型的数据。*/,11.2结构体变量的定义及引用,经以上定义后,结构体类型struct teacher与系统定义的类型int、long、float 等一样,可以用它来定义该类型的变量、数组、函数等。不同的是:结构体类型的作用范围是有限制的。在函数体内定义的结构体类型的作用域为本函数内,在函数体外定义的结构体类型的作用域为本程序文件内,若要引用不在本文件内的结构体类型,通常用#include命令将定义该类型的文件包含进来。例11.1定义一个结构
6、体变量,用于存放一个教师的信息,然后将其输出。,#include stdio.hmain()struct teacher/*该类型的作用范围在本函数内*/char name30;char sex;int age;char position10;float salary;struct teacher person;/*定义结构体变量person*/strcpy(person.name,wang li);person.sex=f;/*给各成员赋值*/person.age=30;strcpy(person.position,middle);person.salary=1600;,printf(n n
7、ame sex age position salary);printf(n%-10s%3c%5d%10s%8.2f,person.name,person.sex,person.age,person.position,person.salary);分析:*本例中结构体类型在函数体内定义,其作用范围在本函数体内。*先定义结构体类型,后定义结构体变量。*对结构体变量输入输出操作、或将基本类型的数据赋给结构体变量时,需分别访问各个基本类型的成员,不能整体赋值或输入输出。如:printf(“%s%c%d%s%f”,person);错!person=“li li”,f,24,“primary”,1000;
8、错!,例11.2 定义一个结构体类型,包含通讯录中的如下信息:姓名、年龄、电话、通讯地址;并定义该类型的变量,输入数据并输出。程序l11_2.c#include stdio.hstruct address_list/*在函数体外定义结构体类型*/char name10;/*该类型的作用域为本程序内*/int age;char tel10;char address40;main()struct address_list s;/*定义变量s,占空间62个字节*/printf(n input name:);gets(s.name);,printf(input age:);scanf(%d,分析:*结
9、构体类型address_list 放在函数体外定义,其作用范围为本程序文件内。*结构体变量的输入输出需逐个访问基本类型的成员,引用方法与同类型的变量、数组相同。,例11.3 定义一结构体类型,包含工人的工资信息:编号、姓名、工资。并定义变量存放两人的数据,然后交换,输出交换前后的信息。程序l11_3.c#include stdio.hmain()struct salary_list char no10;char name30;float salary;struct salary_list z,x=10,li ming,2000,y=12,wang yan,2500;/*结构体变量的初始化,给出
10、各成员的数据并用括起来,各成员之间用逗号分开*/,printf(n no name salary);printf(nx:%-5s%-6s%10.2f,x.no,x.name,x.salary);printf(ny:%-5s%-6s%10.2f,y.no,y.name,y.salary);z=x;x=y;y=z;/*交换两个结构体变量*/printf(nafter exchange:);printf(nx:%-5s%-6s%10.2f,x.no,x.name,x.salary);printf(ny:%-5s%-6s%10.2f,y.no,y.name,y.salary);特别提示:同类型的结构体
11、变量可以整体赋值,即可以将一个结构体变量整体赋给相同类型的另一个结构体变量。,说明一、定义结构体变量,定义结构体变量的方法有三种,(经常用第一种):方法一:先定义结构体类型,再定义结构体变量。如:struct teacher t1,t2;(例11.1)方法二:定义结构体类型的同时定义结构体变量(例11.2)。又如:struct course math long no;4B char name30;math,english;30B 若以后还需用到此结构体类型,定义方法同1:struct course chemic;方法三:格式同方法(二),其中类型名course 省略,缺点是该类型无法再次引用。
12、*结构体变量的存储:系统按结构体类型中各成员的类型给变量分配存储空间。如上图:,no name,说明二、结构体变量的引用,结构体变量定义后,可以分两个层次引用:*访问结构体变量的成员。*引用整个结构体变量。1、使用成员运算符“.”引用结构体变量的成员,“.”的优先级仅次于括号。如例11.1、11.2、11.3中的person.name,person.age x.no,s,salary等成员的赋值、输入、输出。person.age+等价于(person.age)+对结构体变量中的成员的操作与基本类型的变量完全相同。注意:当结构体类型嵌套定义时,应逐级访问。如struct teacher_2 pp
13、;pp.birthday.year=1999;2、相同类型的结构体变量之间可以整体赋值。如例11.3中交换两个结构体变量的值。,11.2结构体数组,结构体数组用于保存一批同类型的结构体数据。每个数组元素相当于一个结构体变量。例:11.4输入三门课的信息(课程编号、课程名),并输出。分析:*每门课的信息包含2项,用结构体类型表示,其成员有两项,即课程编号和课程名。*三门课的信息,用一维数组保存。输入:可以赋初值,也可由用户输入,本例中采用赋初值的方式。程序l11_2_1.c 说明:结构体数组的初始化同基本类型的一维数组,只不过每个数组元素是用括起的一个结构体常量。数组元素中每个成员的访问,同变量
14、的成员的访问。,例11.5输入N个学生的学号、姓名、三门课的成绩,计算每人的平均成绩,并按平均成绩由高到低排序,输出排序后的成绩表。分析:*每个学生的信息包括多项,用结构体类型表示,其成员有学号、姓名、三门课的成绩、平均成绩。N个学生的信息用一维数组保存。*算法:输入并计算平均成绩、排序、输出三步。可将后面两项功能分别用函数实现,在主函数中调用。设排序函数为sort(),输出函数为output().分析每个函数的具体组成:函数类型 参数 函数体程序l11_2_2.c,/*例11.5程序*/struct stu char no8;char name10;float score3;float av
15、er;#include stdio.h#include stdlib.h#define N 3#define STU_SC struct stu,main()void sort(STU_SC sc1),output(STU_SC sc2);STU_SC sc_listN;int i,j;char temp10;float sum;printf(n input no,name,score(13):);for(i=0;iN;i+)printf(nstudent%d:n,i+1);sum=0;printf(no:);gets(sc_listi.no);printf(name:);gets(sc_li
16、sti.name);for(j=0;j3;j+)printf(score%d:,j+1);gets(temp);sc_listi.scorej=atof(temp);sum+=sc_listi.scorej;,sc_listi.aver=sum/3;sort(sc_list);output(sc_list);void sort(STU_SC sc1)int i,j,k;STU_SC t;for(i=0;isc1k.aver)k=j;if(k!=i)t=sc1k;sc1k=sc1i;sc1i=t;,void output(STU_SC sc2)int i;printf(n no name sco
17、re1 score2 score3 aver);for(i=0;iN;i+)printf(n%-8s%-10s,sc2i.no,sc2i.name);printf(%8.2f%8.2f%8.2f%8.2f,sc2i.score0,sc2i.score1,sc2i.score2,sc2i.aver);,通过本例说明:1、结构体数组中的每一个数组元素相当于一个同类型的结构体变量。2、对数组元素的输入输出及运算均需对元素的基本类型的成员进行,只有同类型的数组元素才可以作为整体相互赋值。3、结构体数据的成员通常类型各不相同,输入各成员数据时应注意:,4、关于成员数据的输入技巧:*为了正确读入任意字符串
18、,通常用gets()输入字符串,而不用scanf()。*为了避免多余的回车符影响数据的正常输入,整型、实型的数据也用gets()但作字符串读入,然后再用相应的函数转换成所需类型。如:char t20;gets(t);sc_listi.scorej=atof(t);同理,atoi(t)则转换成整型。*但输入单个字符时,通常用:ch=getchar();getchar();5、本例中,用结构体数组名作函数参数,传递的是数组的首地址,实现主、被调函数共用同一个结构体数组。,11.3指向结构体类型的指针,结构体变量在内存中占据一内存段,该内存段的首地址就是该结构体变量的指针。通过指向结构体类型数据的指
19、针变量可以间接的访问结构体变量,也可以使用指向结构体数据的指针变量访问结构体数组中的元素。例11.3.1定义一结构体类型,表示复数。定义该类型的变量和指针变量,用指针变量访问结构体变量。分析:复数由实部和虚部组成,因此该结构体类型有两个成员:real,image。,L11.3.1程序:main()struct complex/*表示复数的结构体类型*/float real;float image;struct complex x=2,3,*p;p=/*通过p访问x*/运行结果:2+3i 2+3i/*两个输出结果相同*/,23,使用指向结构体数据的指针变量引用结构体成员的一般格式:指针变量名-成
20、员名-为指向运算符,即取指针变量指向的结构体数据的成员。如:p-real 等价于(*p).real 其含义为:取指针变量p指向的结构体数据的成员real。例11.3.2使用指向结构体数组元素的指针引用数组元素:输入输出N个复数。分析:复数用结构体类型表示,输入时用下标法引用数组元素,输出实用指针引用各元素。注意:p+的含义是p指向下一个数组元素。为了引用已定义的结构体类型,通常将结构体类型名定义为更简单的宏名。,struct complex int real;int image;#define COMPLEX struct complex#define N 5main()COMPLEX ssN
21、,*p;int i;printf(n input%d complex:n,N);for(i=0;i”引用成员*/printf(n%d+%di,p-real,p-image);,11.4动态内存分配与链表,动态内存分配的引入问题:如果要在内存中保存一个班学生的数据,按照现有的手段,首先要知道该班的人数,然后据此定义数组,可是,每一个班的人数并不完全相同,并且是浮动的,用确定长度的数组无法保存任意班级学生的数据,如果定义的数组太大,造成内存浪费,太小将导致程序无法实现原功能要求。这显然不够理想和灵活。解决以上问题的方法是使用动态内存分配:在程序运行中实时申请内存分配,申请到的内存在不用时还可以随时
22、释放。这样就可以根据每次运行程序时要处理的数据的多少随时申请内存,因此可以处理任意人数的数据。,用于动态内存分配的和释放的函数:(1)malloc()函数原型:void*malloc(unsigned int size)功能:申请分配size字节的连续内存空间。若分配成功,函数返回所分配内存的首地址;否则返回空指针(NULL)。(2)calloc()函数原型:void*calloc(unsigned int n,unsigned int size)功能:申请分配n各连续的内存空间、每个空间为size字节。若成功,返回所分配内存的首地址;否则返回空指针(NULL)。(3)free()函数原型:v
23、oid free(void*p)功能:释放以前分配到的、指针p指向的内存空间。无返回值。,例11.4.1编程实现:输入一批实验数据(整型),计算其平均值。(个数由用户输入)#include#include main()int n,i,*p;float sum=0;printf(n input datas num:);scanf(%d,printf(n input%d integer:,n);for(i=0;in;i+,p+)scanf(%d,p);sum+=*p;printf(n aver:%f,sum/n);free(p);程序分析:特别提示:(1)使用分配失败是返回的空指针会造成程序的流产
24、或系统的破坏,因此,程序中应先判断分配是否成功,然后再分别做出处理。(2)所分配的首地址应先强制转换为所需的类型,然后使用。(3)动态内存使用后应及时释放,否则,会导致内存资源的枯竭。,链表:是一种重要的数据结构,用来保存一批相同类型的结构体数据。与数组不同的是,链表中的各元素不要求连续存放,而且链表的长度事先不必确定。当要输入数据时,先向系统申请分配一个结构体数据的空间,因此实现链表时要用到动态内存分配技术。由于链表的组成元素-结点,在C语言中是用结构体来实现的,所以链表和链表操作是结构体应用的重要实例。本节讨论最简单的链表:单向链表。单向链表:头指针 头结点 尾结点链表结点:结点是一结构体
25、数据,其成员包括两部分:结点有效数据和结点指针(如图)。结点指针指向下一个结点,由此将所有结点“链”在一起、形成一根数据的“链条”。尾结点的指针为0,即空指针NULL,表示链表的结束。,单向链表的基本操作:建立链表、插入/删除结点、释放结点或整个链表。例11.4 使用链表建立工人的工资单档案:编号、姓名、工资。并能及时添加记录、删除记录、插入记录、输出工资表。分析:首先定义每个记录的结构体类型:struct work long no;char name10;/*有效数据:3项*/float salary;struct work*next;/*指向下一个结点的指针*/;,一、建立单向链表:功能:
26、输入一个或多个结点数据(工人编号输入0时结束),按输入的先后顺序生成链表。返回链表的头指针。函数:struct work*create()结点指针head为头指针,指向第一个结点;指针p1指向新输入的结点,p2指向链表的尾节点。n表示结点的个数。算法:首先输入一个结点数据,若该数据有效(即编号不等于0),n加1;然后将该结点加入链表:若n等于1,则该结点应是头结点,即head=p1;否则,连接在尾结点后面:p2-next=p1;然后p2后移:p2=p1;使得p2始终指向结点。重复以上过程。,申请一个结点空间并输入数据,struct work*input()struct work*q;float
27、 x;q=(struct work*)malloc(LEN);if(!q)printf(n failure.);exit(0);printf(n input a workers information(no=0 for end):n);printf(no:);scanf(%ld,/*返回该结点的首地址*/,建立链表,返回头指针,struct work*create(void)struct work*head,*p1,*p2;int n=0;p1=p2=input();head=NULL;while(p1-no!=0)n+;if(n=1)head=p1;else p2-next=p1;p2=p1
28、;p1=input();/*输入下一个人的信息*/p2-next=NULL;return(head);,二、添加结点,功能:在链表的末尾追加多个结点,直至输入的结点编号等于0为止。函数:struct work*append(struct work*head)算法:指针p指向链表的尾结点,q指向新输入的结点。重复执行:若链表为空,则q指向的结点尾头结点;否则,将q链在p之后。,添加结点之链表尾,struct work*append(struct work*head2)struct work*q,*p=head2;q=input();q-next=NULL;while(q-no!=0)if(hea
29、d2!=NULL)while(p-next!=NULL)p=p-next;p-next=q;else head2=p=q;q=input();q-next=NULL;return(head2);,三、输出链表各结点,算法:指针p指向链表的头结点,然后重复:若p指向的结点不为空,即链表未结束,则输出链表的结点成员;p指向下一个结点。,输出链表各结点,void out_list(struct work*head)struct work*p;printf(n no name salaryn);p=head;if(head=NULL)printf(n no data.);else while(p!=N
30、ULL)printf(n%4ld%10s%7.2f,p-no,p-name,p-salary);p=p-next;,四、按编号删除结点,函数:struct work*delete(struct work*head3,long no)算法:先判断链表是否为空,若是,则结束;否则,从头结点开始查找要删除的结点。若找到,则删除,即将其从链表中去掉,并释放该结点;否则,若到了链表的为结点仍未找到,则输出“not found”。,按编号删除结点,struct work*delete(struct work*head3,int no)struct work*p,*q=head3;if(head3=NULL
31、)printf(n list null.);return(head3);while(no!=q-no,在第n个结点前插入一个结点,分析:变量p指向第n个结点,q指向第n-1个结点,即新结点插在p和q指向的结点之间。分三种情况:1)插在头结点之前。2)插在中间:t-next=p;q-next=t;3)链表中无第n个结点。,在第n个结点前插入一个结点,struct work*insert(struct work*head,int n)int i;struct work*p=head,*q,*t;t=input();if(n=1)t-next=head;head=t;return(head);/*插
32、在表头*/for(i=1;inext!=NULL)q=p;p=p-next;else break;if(ino!=0)q-next=t;t-next=p;return(head);,主函数,#include#include#define LEN sizeof(struct work)main()struct work*head;long num;int n1;head=create();printf(n append:);head=append(head);printf(n insert:);scanf(%d,11.5 共用体,所谓共用体数据类型是指将不同的数据项组织为一个整体,它们在内存中占
33、用首地址相同的一段存储单元。共用体也称为联合。例如:union exam int a;float b;char c;x;,共用体类型说明的一般形式为:union 共用体标识名 类型名1 共用体成员名表1;类型名2 共用体成员名表2;类型名n 共用体成员名表n;其中union是关键字,是共用体类型的标志。共用体中的成员可以是简单变量,也可以是数组、指针、结构体和共用体。,11.5.2共用体变量的定义及引用,定义共用体变量的方式与结构体变量相似。1定义类型的同时定义变量。union data int i;char ch;float f;a,b,c;2将类型定义与变量定义分开。union data
34、int i;char ch;float f;;union data a,b,c;,3直接定义共用体变量。union int i;char ch;float f;a,b,c;可以引用一个共用体变量中的某个成员,引用方式与引用结构体变量中的成员相似。例如:a.i=5;a.ch=b;a.f=8.6;特别提示:一个共用体变量不是同时存放多个成员的值,某一时刻只能存放其中的一个成员的值,这就是最后赋予它的值。比如,执行上面三个赋值语句后,变量a中存的是实型数8.6,如果此时执行语句printf(%d,%c,%f,a.i,a.ch,a.f);输出的分别是:a.f中最低两个字节(a.i的值)、最低字节的值(
35、a.ch)、a.f的值。,例11.9:利用共用体的特点分别取出int变量中高字节和低字节中的两个数。#include union word char ch2;int n;typedef union word WORD;main()WORD w;printf(输入:);scanf(%d,运行结果:输入:24930低位=98高位=97,11.6 枚举类型,所谓“枚举”是指将变量的值一一列举出来,变量的值只限于列举出来的值的范围内。例如:enum weekdaySunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday;说明:enum是关键字,标
36、识枚举类型,定义枚举类型必须用enum开头。在C编译中,枚举类型中的元素按常量处理,称为枚举常量,不能对它们赋值。,枚举常量是有值的,C语言按定义时的顺序定义它们的值为0,1,2,;也可以在定义时指定枚举常量的值。例如:enum weekday(Sunday=7,Monday=1,Tuesday,Wednesday,Thursday,Friday,Saturday);指定枚举常量的值后,其后的元素的之依次递增,如定义Sunday=7,Monday=1,则Tuesday的值顺序加1,即Tuesday=2,Saturday=6。枚举值可以用来作判断比较,按其值大小进行比较。如:if(today=Monday)printf(Monday);一个整数不能直接赋给一个枚举变量,应先进行强制类型转换才能赋值。例如:若定义:enum weekday day;则 day=(enum weekday)3;等价于:day=Wednesday;,11.7 用typedef定义类型,