资源描述
单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,*,第7章结构体、共用体与枚举,教学目标,:,通过本章学习,要求学生能熟练运用结构体这一新,的数据结构编制较复杂的应用程序,并掌握链表的建立、,插入、删除、查找等操作的算法。,本讲重点,:,结构体变量的定义、引用、初始化;,用指针处理链表;,typedef,自定义结构体类型的方法。,本讲难点,:,用指针处理链表。,教学方法,:,理论教学与实践教学相结合。,第七章 结构体、共用体与枚举,7.1 结构体类型与变量,7.2 结构体数组,7.3 结构体类型数据的指针,7.4 结构体位段,7.5 共用体类型与变量,7.6 枚举类型与变量,7.7程序设计举例,7.1 结构体类型与变量,7.1.1 结构体类型定义,结构体类型定义的一般形式为:,struct,结构体类型名,类型1 成员1;,类型2 成员2;,类型,n,成员,n;,;,例如:,struct student,int num;,char name20;,char sex;,int age;,float score;,char addr30;,;,说明:,1.结构体类型由“struct 结构体类型名统一说明和引用。,2.只有变量才分配地址,类型定义并不分配内存空间。,3.结构体中说明的各个成员类似于以前的变量,但在类型定义时不分配地址。,4.相同类型的成员可以合在一个类型下说明。如,struct student,int num,age;,char name20,sex,addr30;,float score;,;,5.最后一定要以分号结束。,6.可以嵌套定义,即在结构体类型定义中又有结构体类型的成员。如,struct student,int num,age;,char name20,sex,addr30;,struct,float Chinese,Math,Physics,English;,score;/*,无名结构体类型定义的成员,score*/,;,7.,结构体类型有作用范围的。即,它与变量一样,有全局和局部之分。在一个函数中定义的结构体类型是局部的,只能用于在该函数中定义结构体变量;在函数之外定义的结构体类型是全局的,可定义在其后用到的结构体类型的全局和局部变量。,7.1.2,结构体变量的定义和引用,定义结构体类型变量有如下三种形式(以上面的结构体类型,student,为例):,1.定义结构体类型之后再定义结构体类型变量,如,struct student a,b,c;,定义了三个结构体类型变量,a、b,和,c。,2.,定义结构体类型同时定义结构体类型变量,如,struct student,int num;,char name20;,char sex;,int age;,float score;,char addr30;,a,b,c;,也定义了三个结构体类型变量,a、b,和,c。,3.,定义无名结构体类型同时定义结构体类型变量,如,struct,int num;,char name20;,char sex;,int age;,float score;,char addr30;,a,b,c;,也定义了三个结构体类型变量,a、b,和,c。,但这种方法只能在,此定义变量,因为没有类型名称,所以这种结构体类型无法,重复使用。,无论哪种定义变量的形式,系统都将给已定义的变量分配地址。分配内存空间的大小为各个成员所占地址空间的总和,并且各个成员变量是按类型定义的顺序分配地址空间的。那么如何使用各个成员变量呢?,成员的引用方式:一般地,要引用到最底层的成员。,结构体类型变量名.成员变量名.成员变量名.,【例7.1】结构体成员引用举例。,main(),struct student,int num,age;,char name20,sex,addr30;,struct,float Chinese,Math,Physics,English;,score;,a=4102,21,Li Ping,F,Beijing Road 11#,87,63,54,72;,printf(No.:%dnName:%snSex:%cnAddress:%sn,a.num,a.name,a.sex,a.addr);,printf(Chinese:%5.1fnMath:%5.1fnPhisics:%5.1fnEnglish:%5.1fn,a.score.Chinese,a.score.Math,a.score.Physics,a.score.English);,运行结果:,No.:4102,Name:Li Ping,Sex:F,Address:Beijing Road 11#,Chinese:87.0,Math:63.0,Physics:54.0,English:72.0,在从键盘读入数据给变量赋值时,也要按地址方式进行。如,scanf(%d,scanf(%s,a.name);,scanf(%c,scanf(%f,结构体变量还可以整体赋值,将上面程序修改如下,运行结果,是一样的。,main(),struct student,int num,age;,char name20,sex,addr30;,struct,float Chinese,Math,Physics,English;,score;,a=4102,21,Li Ping,F,Beijing Road 11#,87,63,54,72,b;,b=a;/*,结构体变量的整体赋值*/,printf(No.:%dnName:%snSex:%cnAddress:%sn,b.num,b.name,b.sex,b.addr);,printf(Chinese:%5.1fnMath:%5.1fnPhysics:%5.1fn,English:%5.1fn,b.score.Chinese,b.score.Math,b.score.Physics,b.score.English);,7.2 结构体数组,和普通数组一样,我们也可以定义结构体数组。那么,结构体数组中的每个分量就相当于一个结构体变量了,用法自然与普通结构体变量相同。通常地,结构体数组都是一维的,当然,也可以定义多维的。结构体数组相当于一张二维数据表,表的横向相当于记录,而列向可以称为属性,用于描述每条记录的所有信息。比方,下面的表格就可以用结构体数组处理。,【例7.2】输出上述表格信息。,#define N 3,struct student,char num7,name7;,int com,eng,total;,;,main(),int i;,struct student stuN=202149,王学海,83,77,0,203120,刘玉芳,89,72,0,202134,邱玲,76,68,0;,printf(学号t姓名t计算机成绩t英语成绩t总成绩n);,for(i=0;iN;i+),stui.total=stui +stui.eng;,printf(%st%st%dtt%dtt%dn,stui.num,stui.name,stui ,stui.eng,stui.total);,运行结果:,学号姓名计算机成绩英语成绩总成绩,202149王学海8377160,203120刘玉芳8972161,202134邱玲7668144,【例5.3】统计候选人得票数。假设有三个候选人,由10个,选民参加投票选出一个代表。,#,include string.h,struct person,char name20;,int count;,leader3=li,0,zhang,0,xue,0;,main(),int i,j;,char select20;,for(i=0;i10;i+),printf(%dtPlease input your result:,i+1);,scanf(%s,select);,for(j=0;j3;j+),if(strcmp(leaderj.name,select)=0),leaderj.count+;,printf(-=The result=-n);,for(j=0;j。,引用方法如下:,(*结构体指针变量名).成员名,/*,圆括号不能省略*/,。,结构体指针变量名-成员名,【例7.4】指向结构体变量的指针的应用。,#,include string.h,main(),struct student,long num;,char name20,sex;,float score;,;,struct student stu,*p;,p=,stu.num=89101;,strcpy(stu.name,Li Lin);,stu.sex=,M,;,stu.score=89.5;,printf(No.:%ldnname:%snsex:%cnscore:%fn,(*p).num,(*p).name,(*p).sex,(*p).score);,printf(No.:%ldnname:%snsex:%cnscore:%fn,p-num,p-name,p-sex,p-score);,运行结果:,No.:89101,name:Li Lin,sex:M,score:89.500000,No.:89101,name:Li Lin,sex:M,score:89.500000,在存取结构体变量中的成员值时,有如下三种方式:,1.结构体变量名.成员名,2.(*结构体指针变量名).成员名,3.结构体指针变量名-成员名,7.3.2,结构体数组的指针与指针变量,【例7.5】结构体数组指针举例。,struct student,int num;,char name20,sex;,int age;,;,struct student stu3=10101,Li Lin,M,18,10102,Wan Fen,M,19,10104,Liu Min,F,20;,main(),struct student*p;,printf(No.tNametSextAgen);,for(p=stu;pnum,p-name,p-sex,p-age);,运行结果:,No.NameSexAge,10101Li LinM18,10102Wan FenM19,10104Liu MinF20,由于,p,是指向结构体数组的指针变量,所以,p+,是按数组元素的长度移动的,即它每次移动都是移动到数组中各个结构体分量的首地址。,7.3.3,结构体指针与变量作函数参数,与普通变量一样,结构体指针和变量也可以作为函数的参数,进行值的传递,略有不同的是结构体变量的成员也可以作为,函数的参数。函数的返回值也可以是结构体类型的数据。,【例7.6】找出例7.5中的第一个女生的记录。,#,include stdio.h,struct student,int num;,char name20,sex;,int age;,;,struct student stu3=10101,Li Lin,M,18,10102,Wan Fen,M,19,10104,Liu Min,F,20;,main(),struct student*p,*search();,p=search(stu,3);,printf(%dt%st%ct%dn,p-num,p-name,p-sex,p-age);,struct student*search(struct student*s,int n),struct student*q=s;,for(;qsex=,F,)return q;,return NULL;,运行结果:,10104,Liu MinF20,7.3.4 自定义类型,除可直接使用提供的标准类型和自定义的类型结构、共用、枚举外,也可使用typedef定义已有类型的别名。该别名与标准类型名一样,可用来定义相应的变量。定义已有类型别名的方法如下:1按定义变量的方法,写出定义体;2将变量名换成别名;3在定义体最前面加上typedef。例如:,给实型float定义1个别名REAL。1按定义实型变量的方法,写出定义体:float f;2将变量名换成别名:float REAL;3在定义体最前面加上typedef:typedef float REAL;,给如下所示的结构类型struct date定义1个别名DATE。,struct date,int year,month,day;,;,1按定义结构变量的方法,写出定义体:,struct date d;,2将变量名换成别名:struct date DATE;,3在定义体最前面加上typedef:,typedef struct date,int year,month,day;,DATE;,例如,自定义一个数组类型名ARRAY:,1.写出定义体:int a100;,2.将变量名换成别名:int ARRAY100;,3.在定义体最前面加上typedef:,typedef int ARRAY100;,用新类型名定义变量 ARRAY a,b,c;,标识符a、b和c都被定义成具有100个分量的整型一维数组。,相当于下面定义形式:int a100,b100,c100;,说明:,用typedef只是给已有类型增加个别名,并不能创造个,新的类型。就如同人一样,除学名外,可以再取一个小名,或雅号,但并不能创造出另一个人来。,7.4 结构体位段,以前介绍的数据存取都是以字节为单位的。实际上,有时存储一个数据不必用一个或多个字节,只须使用一个或几个二进制位即可。例如,存放真假值,只须使用一个二进制位存放1或0即可。这样,常常在一个字节中可以存放多个数据,从而节省大量的存储空间。使用二进制位来存储信息可以有很多种方法,其中最主要的一种就是位段。,C语言允许在一个结构体上以二进制位为单位来指定其成员所占空间的长度,这种以位为单位的成员就称为“位段或“位域(bit field)。,例如:,struct packed_data,unsigned a:2;,/*,指定成员,a,占2位*/,unsigned b:6;,/*,指定成员,b,占6位*/,unsigned c:5;,/*,指定成员,c,占5位*/,unsigned d:3;,/*,指定成员,d,占3位*/,int i;,/*,指定成员,i,为整型,占16位*/,data;/,*,定义结构体类型同时定义了一个变量,data*/,其内存分配为:,a、b、c、d,分别占2位、6位、5位、3位。,i,为整型成员,占2个字节。这样,变量,data,共占4个字节,,见图7.1。,由于位段本身是在一个结构体中,所以其成员的存取方法与,结构体数据的存取方法相同。,如果,data.a=2,data.b=37,data.c=28,data.d=6,data.i=300,,那么内存数据图见图7.2。,在给位段成员赋值时,一定要注意成员的取值范围。比方,a占两位,其取值范围为:0011,即03。如果数据超出了它们的取值范围,将会丧失。,使用位段时应注意以下几点:,1.一个位段长度不能超过一个存储单元(通常为2字节)。,2.一个位段不能跨越两个存储单元。本单元剩余二进制位如果存放不下一个位段时,那么省略不用,这个位段自动放到下一存储单元开头位置。,3.用户不想用的二进制位可以定义成无名位段。如,,struct packed_data,unsigned a:2;,unsigned :6;,/*,指定无名位段,占6位,不用*/,unsigned c:5;,unsigned d:3;,int i;,data;,4.,设置长度为0的位段,使得下一个位段从下一字节开始存放。,struct packed_data,unsigned a:2;,unsigned :0;,/*,指定长度为0位段,使得位段,c,从下一字节开始*/,unsigned c:5;,unsigned d:3;,int i;,data;,5.位段数据可以按整型数据引用和输出。,6.不能定义位段数组。,7.位段成员的类型必须指定为,unsigned int。,【例7.7】位段操作举例。,typedef struct,unsigned int f1:1;,unsigned int f2:2;,unsigned int :0;,unsigned int data:4;,bitfield;,main(),bitfield btf;,btf.f1=1;,btf.f2=2;,btf.data=9;,printf(sizeof(btf)=%dn,sizeof(bitfield);,printf(btf.f1=%dn,btf.f1);,printf(btf.f2=%dn,btf.f2);,printf(btf.data=%dn,btf.data);,printf(btf=%xHn,btf);,printf(nPlease input 16 bit hexidecimal number:);,scanf(%4x,printf(btf.f1=%dn,btf.f1);,printf(btf.f2=%dn,btf.f2);,printf(btf.data=%dn,btf.data);,printf(btf=%dn,btf);,运行结果:,sizeof(btf)=2,btf.f1=1,btf.f2=2,btf.data=9,btf=9d5H,Please input 16 bit hexidecimal number:,9ae4,btf.f1=0,btf.f2=2,btf.data=10,btf=-25884,7.5 共用体类型与变量,有时需要将不同类型的数据存放到同一段内存单元中。需要什么类型的数据这里就存放什么类型的数据。这些数据的起始地址都是相同的,数据之间相互覆盖,只有最后一次存入的数据才是有效的。这种几个不同的变量占用同一段内存的结构称为“共用体类型的结构。,7.5.1 共用体类型定义,共用体类型定义的一般形式为:,union 共用体类型名,类型1 成员1;,类型2 成员2;,类型n 成员n;,;,例如:,union data,int i;,char ch;,float f;,;,从定义形式上来看,它同结构体极为相似。所不同的是它说明的几个成员不像结构体那样顺序存储,而是叠放在同一个地址开始的空间上,见图7.3。共用体类型的长度为最大成员所占空间的长度。比方,上面的union data类型的长度为4个字节,也就是float类型所占的空间长度。,图7.3,7.5.2,共用体变量的定义和引用,同结构体变量类似,定义共用体类型变量也有如下三种形式(以上面的共用体类型,union data,为例):,1.定义共用体类型之后再定义共用体类型变量,如,union data a,b,c;,定义了三个共用体类型变量,a、b,和,c。,2.,定义共用体类型同时定义共用体类型变量,如,union data,int i;,char ch;,float f;,a,b,c;,也定义了三个共用体类型变量,a、b,和,c。,3.定义无名共用体类型同时定义共用体类型变量,如,union,int i;,char ch;,float f;,a,b,c;,也定义了三个共用体类型变量a、b和c。但这种方法只能在此定义变量,因为没有类型名称,所以这种形式的类型无法重复使用。,共用体成员的引用方式与结构体成员的引用方式没有差异,一般地,也要引用到最底层的成员。成员引用形式为:,共用体类型变量名.成员变量名.成员变量名.,【例7.8】共用体类型举例。,union data,int i;,char ch;,float f;,;,main(),union data ua;,ua.i=10;,ua.ch=,A,;,ua.f=3.14;,printf(i=%dtch=%ctf=%ftua=%fn,ua.i,ua.ch,ua.f,ua);,运行结果为:,i=-2621ch=f=3.140000ua=49.920021,只有最后一次赋值的成员,f,是有效的值。,注意:,共用体变量不能整体赋值,也不可以对共用体变量进行初始化处理;共用体变量不能作函数参数传递,也不能使函数返回一个共用体类型的数据;但可以使用共用体类型的指针和变量进行参数传递和成员引用等,其用法同结构体类型。,7.6 枚举类型与变量,7.6.1 枚举类型定义,枚举类型定义也根本上同结构体和共用体类型定义,它的一般格式为:,enum 枚举类型名,枚举常量1=序号1,,枚举常量2=序号2,,枚举常量n=序号n,;,其中,枚举常量是一种符号常量,也称为枚举元素,要符合标识符的起名规那么。序号是枚举常量对应的整数值,可以省略,省略序号那么按系统规定处理。,注意类型定义中各个枚举常量之间要由逗号间隔,而不是,分号,最后一个枚举元素的后面无逗号。,例如,有如下类型定义:,enum weekday sun,mon,tue,wed,thu,fri,sat;,这里,列出了枚举类型enum weekday所有可能的7个值。,省略序号,系统默认从0开始连续排列。,如上面的枚举类型中,sun对应0、mon对应1、sat对应6。,如果遇到有改变的序号,那么序号从被改变位置开始连续递增。,例如,假设把上面的枚举类型改为下面的形式:,enum weekday sun,mon=6,tue,wed,thu=20,fri,sat;,那么7个枚举元素的序号依次为:0、6、7、8、20、21、22。,7.6.2,枚举变量与枚举元素,同结构体与共用体变量类似,定义枚举类型变量也有如下三种形式(以上面的枚举类型,enum weekday,为例):,1.定义枚举类型之后再定义枚举类型变量,如,enum weekday yesterday,today,tomorrow;,2.定义枚举类型同时定义枚举类型变量,如,enum weekday sun,mon,tue,wed,thu,fri,sat yesterday,today,tomorrow;,3.,定义无名枚举类型同时定义变量,如,enum sun,mon,tue,wed,thu,fri,sat yesterday,today,tomorrow;,这种类型无法重复使用,说明1枚举型仅适应于取值有限的数据。,例如,根据现行的历法规定,周天,年个月。,2取值表中的值称为枚举元素,其含义由程序解释。,例如,不是因为写成“Sun就自动代表“星期天。,事实上,枚举元素用什么表示都可以。,3枚举元素可以进行比较。,4不允许直接把一个整数赋值给一个枚举变量,如,today=5是非法的,,应该进行强制类型转换,,写成:today=(enum weekday)5;,5枚举元素的值是不能直接输出的,通常要用到switch语句与字符串输出语句结合起来才行。,6枚举型变量的取值仅限于值表中的值。,【例7.9】口袋中有红、黄、蓝、白、黑5种颜色的球假设干个。,每次从口袋中取出3个球,问得到3种不同颜色的球的所有可能,取法,并打印出每种组合的3种颜色。,main(),enum colorred,yellow,blue,white,black;,enum color i,j,k,pri;,int n,loop;,n=0;,for(i=red;i=black;i+),for(j=red;j=black;j+),if(i!=j),for(k=red;k=black;k+),if(k!=i)&(k!=j),printf(%-4d,+n);,for(loop=1;loop=3;loop+),/*,循环三次,输出三种不同颜色的球*/,switch(loop),case 1:pri=i;break;,case 2:pri=j;break;,case 3:pri=k;break;,defult:break;,7.7 程序设计举例,【例7.10】将学生成绩表(如表5-1)按总成绩降序排列。,#define N 4,struct student,char num7,name7;,int com,eng,total;,;,main(),int i;,struct student stuN=,202149,王学海,83,77,0,203120,刘玉芳,89,72,0,202134,邱玲,76,68,0,200537,李红梅,82,74,0;,sorttotal(stu,N);,printf(学号t姓名t计算机成绩t英语成绩t总成绩n);,for(i=0;iN;i+),stui.total=stui +stui.eng;,printf(%st%st%dtt%dtt%dn,stui.num,stui.name,stui ,stui.eng,stui.total);,sorttotal(struct student s,int n),int i,j,k;,struct student t;,for(i=0;in;i+),si.total=si +si.eng;,for(i=0;in-1;i+),for(k=i,j=i+1;jn;j+),if(sk.totalname,p-age,p-addr);,main(),person s;,printf(Name:t);gets(s.name);,printf(Age:t);scanf(%d,getchar();/*,读走上一个数据键盘输入的回车符,,以免影响下一个字符串的输入*/,printf(Address:);gets(s.addr);,print(,运行结果,:,Name:,Liu Wenbin,Age:,24,Address:,Beijing Road 11,Name Age Address,Liu Wenbin 24 Beijing Road 11,
展开阅读全文