[其它考试]计算机二级必备之 第07章 结构体与共用体.ppt
1,第七章结构体、共用体和枚举,2,本章要点,结构体类型的定义和引用结构体数组指向结构体变量的指针共用体枚举,3,问题的引出,在日常生活中,我们常常会遇到一些需要填写的登记表,如住宿表、成绩表、通讯地址等等。例如:填写一个学生的学号、姓名、性别、年龄和地址等信息。int num;char name20;char sex;int age;float score;char addr30,应当把它们组织成一个组合项,在一个组合项中包含若干个类型不同(当然也可以相同)的数据项。,4,7.1.1 结构体类型的定义,定义一个结构体类型的一般形式为:struct 结构体名 成员表列;如:struct student int num;char name20;char sex;int age;float score;char addr30;,结构体名,类型名,成员名,7.1 结构体类型的定义和引用,5,7.1.2 结构体类型变量的定义,可以采取以下3种方法定义结构体类型变量:(1)先定义结构体类型,再定义变量名例如:struct student student1,student2;|结构体类型名 结构体变量名 定义了student1和student2为struct student类型的变量,即它们具有struct student类型的结构。,6,当定义了结构体类型后,系统不分配内存空间,只有当定义了结构体类型变量之后,系统才会为之分配内存空间。例如:struct student int num;char name20;char sex;int age;float score;char addr30;struct student student1,student2;student1和student2在内存中各占59个字节(2+20+1+2+4+30=59)。,7,(2)在声明类型的同时定义变量 定义的一般形式如下:struct结构体名 成员表列 变量名表列;,例如:struct student int num;char name20;char sex;int age;float score;char addr30;student1,student2;,8,(3)直接定义结构体类型变量 其一般形式为:struct 成员表列变量名表列;即不出现结构体名。,注意:(1)类型与变量是不同的概念,不要混同。我们只能对变量赋值、存取或运算,而不能对一个类型赋值、存取或运算。在编译时,对类型是不分配空间的,只对变量分配空间。,注意:(2)对结构体中的成员,可以单独使用,它的作用与地位相当于普通变量。(3)成员也可以是一个结构体变量。(4)成员名可以与程序中的变量名相同,二者不代表同一对象。,9,例如:struct date int month;int day;int year;struct employee char name20;struct date birthday;char department30;char address30;long box;long phone;char email30;employee1,employee2;,10,由于结构体类型变量汇集了各类不同数据类型的成员,所以结构体类型变量的初始化就略显复杂。结构体类型变量的定义和初始化为:struct stu char name20;char sex;long num;float score3;struct stu student=“liping”,f,970541,98.5,97.4,95;,7.1.3 结构体类型变量的初始化,struct stu student=“liping”,f,970541,98.5,97.4,95;,11,7.1.4 结构体类型变量的引用,引用结构体变量中成员的方式为:结构体变量名.成员名例如,定义结构体类型及变量如下:struct data int day;int month;int year;time1,time2;,time1.day表示time1变量中的day成员,即time1的day项。可以对变量的成员赋值,例如:time1.day=10;“.”是成员(分量)运算符,它在所有的运算符中优先级最高,因此可以把time1.day作为一个整体来看待。上面赋值语句的作用是将整数10赋给time1变量中的成员day。,12,注意:如果成员本身又属一个结构体类型,则要用若干个成员运算符,逐级地找到最低一级的成员。只能对最低级的成员进行赋值或存取以及运算。,例如,对定义的结构体变量employee1,可以这样访问各成员:employee1.nameemployee1.birthday.day,struct date int month;int day;int year;struct employee char name20;struct date birthday;char department30;char address30;long box;long phone;char email30;employee1,employee2;,13,应遵守以下规则:不能将一个结构体变量作为一个整体进行输入和输出。例如,已定义student1和student2为结构体变量并且它们已有值。不能这样引用:printf(“%d,%s,%c,%d,%f,%sn”,student1);只能对结构体变量中的各个成员分别进行输入和输出。(2)对结构体变量的成员可以像普通变量一样进行各种运算(根据其类型决定可以进行的运算)。例如:student2.score=student1.score;sum=student1.score+student2.score;student1.age+;+student1.age;(3)可以引用结构体变量成员的地址,也可以引用结构体变量的地址如:scanf(%d“,但不能用以下语句整体读入结构体变量,如:scanf(%d,%s,%c,%d,%f,%s“,&student1);,14,7.2 结构体类型数组的定义和使用,一个结构体变量中可以存放一组数据(如一个学生的学号、姓名、成绩等数据)。如果有个学生的数据需要参加运算,显然应该用数组,这就是结构体数组。结构体数组与以前介绍过的数值型数组不同之处在于每个数组元素都是一个结构体类型的数据,它们又都分别包括各个成员(分量)项。,15,7.2.1 定义结构体数组 和定义结构体变量的方法相仿,只需说明其为数组即可。例如:struct student int num;char name20;char sex;int age;float score;char addr30;struct student stu3;,以上定义了一个数组stu,数组有个元素,均为struct student类型数据。,16,也可以直接定义一个结构体数组,例如:struct student int num;stu3;或:strcut int num;stu3;,17,7.2.2 结构体数组的初始化 与其他类型的数组一样,对结构体数组可以初始化。例如:struct student int num;char name20;char sex;int age;float score;char addr30;stu2=10101,LiLin,M,18,87.5,103 BeijingRoad,10102,Zhang Fun,M,19,99,130 Shanghai Road;,18,当然,数组的初始化也可以用以下形式:struct student int num;struct studentstr=,;即先声明结构体类型,然后定义数组为该结构体类型,在定义数组时初始化。,结构体数组初始化的一般形式是在定义数组的后面加上“初值表列;”。,19,7.2.3 结构体数组应用举例,例2:对候选人得票的统计程序。设有3个候选人,每次输入一个得票的候选人的名字,要求最后输出各人得票结果。#include#include struct person char name20;int count;leader3=“Li”,0,”Zhang”,0,”Fun”,0;,20,void main()int i,j;char leader_name20;for(i=1;i=10;i+)scanf(“%s”,leader_name);for(j=0;j3;j+)if(strcmp(leader_name,leaderj.name)=0)leaderj.count+;printf(“n”);for(i=0;i3;i+)printf(“%5s:%dn”,leaderi.name,leaderi.count);,运行结果::,21,7.3 结构体指针的定义和引用,指针变量非常灵活方便,可以指向任一类型变量的起始地址。同样,若定义指针变量指向结构体类型变量,则表示指向该结构体变量的起始地址,因此也可以通过指针来引用结构体类型变量。7.3.1 指向结构体类型变量的使用 下面通过一个简单例子来说明指向结构体变量的指针变量的应用。,22,例指向结构体变量的指针的应用#include#include void main()struct student long num;char name20;char sex;float score;struct student stu_1;struct student*p;p=,printf(No.:%ldnname:%snsex:%cn score:%fn,stu-1.num,stu-1.name,stu-1.sex,stu-1.score);printf(No.:%ldnname:%snsex:%cn score:%fn,(*p).num,(*p).name,(*p).sex,(*p).score);,运行结果:89101 name:LiLin sex:score:89.500000:89101 name:LiLin sex:score:89.500000,23,程序分析:在函数的执行部分将结构体变量_的起始地址赋给指针变量,也就是使指向-,然后对_的各成员赋值。第一个函数是输出_的各个成员的值。用_ 表示_中的成员,依此类推。第二个函数也是用来输出_各成员的值,但使用的是(*)这样的形式。注意*p两侧的括弧不可省,因为“”优先于“*”运算符,*p.num就等价于*(p.num)了。,图11-7,24,在C语言中,为了使用方便和使之直观,可以把(*p)num改用p-num来代替,它表示*p所指向的结构体变量中的num成员。同样,(*p).name等价于p-name。也就是说,以下三种形式等价:结构体变量成员名(*p)成员名 p-成员名 上面程序中最后一个printf函数中的输出项表列可以改写为p-num,p-name,p-sex,p-score。,请分析以下几种运算:-得到指向的结构体变量中的成员的值。-得到指向的结构体变量中的成员的值,用完该值后使它加。-得到指向的结构体变量中的成员的值加,然后再使用它。,25,例4 指向结构体数组的指针的应用#include struct student int num;char name20;char sex;int age;struct student str3=10101,Li Lin,M,18,10102,Zhang Fun,M,19,10104,WangMing,F,20;,void main()struct student*p;printf(No.Name sex age);for(str;str;p)printf(%5d%-20s%2c%4dn,p-num,p-name,p-sex,p-age);,运行结果:LiLin 18 Zhang Fun 19 WangMing 20,26,程序分析:是指向struct student结构体类型数据的指针变量。在for语句中先使的初值为stu,也就是数组stu第一个元素的起始地址。在第一次循环中输出stu0的各个成员值。然后执行,使自加。加意味着p所增加的值为结构体数组stu的一个元素所占的字节数。执行+后p的值等于stu 1,指向stu1。在第二次循环中输出stu1的各成员值。在执行后,p的值等于stu+2,再输出stu 2的各成员值。在执行+后,的值变为stu+,已不再小于stu+3了,不再执行循环。,图11-8,27,注意:(1)如果的初值为stu,即指向第一个元素,则加后p就指向下一个元素。例如:(+p)-num先使自加,然后得到它指向的元素中的num成员值。(p+)-num先得到-num的值,然后使自加,指向stu1。请注意以上二者的不同。,28,注意:(2)程序已定义了是一个指向struct student类型数据的指针变量,它用来指向一个struct student类型的数据,不应用来指向stu数组元素中的某一成员。例如:1a;如果要将某一成员的地址赋给p,可以用强制类型转换,先将成员的地址转换成p的类型。例如:p=(struct student*)stu0.name;,29,结构体与函数 将一个结构体变量的值传递给另一个函数,有3个方法:(1)用结构体变量的成员作参数。(2)用结构体变量作实参。(3)用指向结构体变量(或数组)的指针作实参,将结构体变量(或数组)的地址传给形参.,30,例5 有一个结构体变量stu,内含学生学号、姓名和3门课程的成绩。要求在main函数中赋予值,在另一函数print中将它们输出。今用结构体变量作函数参数。#include#define FORMAT%dn%sn%fn%fn%fn struct student int num;char name20;float score3;,31,void main()void print(struct student);struct student stu;stu.num=12345;strcpy(stu.name,LiLin);stu.score0=67.5;stu.score1=89;stu.score2=78.6;print(stu);void print(struct student stu)printf(FORMAT,stu.num,stu.name,stu.score0,stu.score1,stu.score2);printf(n);,运行结果:12345LiLi67.50000089.00000078.599998,32,例6 将上题改用指向结构体变量的指针作实参。#include#define FORMAT%dn%sn%fn%fn%fn struct student int num;char name20;float score3;stu=12345,LiLi,67.5,89,78.6;void main()void print(struct student*);print(,运行结果:12345LiLi67.50000089.00000078.599998,void print(struct student*p)printf(FORMAT,p-num,p-name,p-score0,p-score1,p-score2);printf();,33,7.5 共用体,7.5.1 共用体的定义共用体的概念 使几个不同的变量共占同一段内存的结构称为“共用体”类型的结构。,定义共用体类型变量的一般形式为:union共用体名 成员表列变量表列;,34,例如:union data union data int i;int i;char ch;或 char ch;float f;float f;a,b,c;union data a,b,c;,35,共用体和结构体的比较:结构体变量所占内存长度是各成员占的内存长度之和。每个成员分别占有其自己的内存单元。共用体变量所占的内存长度等于最长的成员的长度。,例如:上面定义的“共用体”变量、各占个字节(因为一个实型变量占个字节),而不是各占个字节。,36,7.5.2共用体变量的引用方式 只有先定义了共用体变量才能引用它,而且不能引用共用体变量,而只能引用共用体变量中的成员。,例如:前面定义了a、b、c为共用体变量 a.i(引用共用体变量中的整型变量)a.ch(引用共用体变量中的字符变量)a.f(引用共用体变量中的实型变量),37,共用体类型数据的特点(1)同一个内存段可以用来存放几种不同类型的成员,但在每一瞬时只能存放其中一种,而不是同时存放几种。(2)共用体变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员就失去作用。(3)共用体变量的地址和它的各成员的地址都是同一地址。,38,(4)不能对共用体变量名赋值,也不能企图引用变量名来得到一个值,也不能在定义共用体变量时对它初始化。(5)不能把共用体变量作为函数参数,也不能使函数带回共用体变量,但可以使用指向共用体变量的指针。(6)共用体类型可以出现在结构体类型定义中,也可以定义共用体数组。反之,结构体也可以出现在共用体类型定义中,数组也可以作为共用体的成员。,39,例 设有若干个人员的数据,其中有学生和教师。学生的数据中包括:号码、姓名、性别、职业、班级。教师的数据包括:号码、姓名、性别、职业、职务。可以看出,学生和教师所包含的数据是不同的。现要求把它们放在同一表格中。,图11-25,40,#include struct int num;char name10;char sex;char job;union int banji;char position10;category;person2;,41,void main()int i;for(i=0;i2;i+)scanf(“%d%s%c%c”,for(i=0;i2;i+)if(personi.job=S)printf(“%-6d%-10s%-3c%-3c%-6dn”,personi.num,personi.name,personi.sex,personi.job,personi.category.banji);else printf(“%-6d%-10s%-3c%-3c%-6sn”,personi.num,personi.name,personi.sex,personi.job,personi.category.position);,运行情况如下:101 Li f s 501102 Wang m t professorNo.Name sex job class/position101 Li f 501102 Wang m professor,42,7.6 枚举类型,在编写程序时,往往会遇到某些变量只能在一个有限的范围内取值的情况。枚举是一个有名字的整型常量的集合,该类型的变量只能取集合中列举出来的合法值。,7.6.1 枚举类型的定义与共同体类型类似,在定义枚举类型变量之前要先给出每局类型的定义。,43,枚举类型定义的一般形式如下:enum 枚举类型名 枚举值列表;其中,enum是标志枚举类型的关键字,枚举值列表中的各个值用“,”隔开。例如,定义一个表示颜色的枚举类型Color,可以写成下面的形式:enum Color red,green,blue,yellow,white;,其中,Color是枚举类型名,花括号中的red、green、blue、yellow和white是构成该类型的各个成分,即枚举元素。系统为每个枚举元素定义了一个枚举值表示该元素的序号,默认从0开始,即red的枚举值为0,green的枚举值为1,blue的枚举值为2,yellow的枚举值为3,white的枚举值为4。,44,但是,如果规定某个枚举元素的值为指定的整常数,那么该枚举元素后面的元素的序号将接着那个整常数继续增加。,例如,对于下面的枚举类型Color的定义:enum Color red,green,blue=5,yellow,white;,由于在定义枚举类型Color时规定blue的值为5,因此,对于blue之前的枚举元素red和green,编译系统照常从0开始赋予枚举值,而对blue之后的各枚举元素则从6开始赋予枚举值。因而在枚举类型Color中,red的值为0,green的值为1,blue的值为5,yellow的值为6,而white的值为7。,需要注意的是,枚举值是常量,而不是变量,不能在程序中用赋值语句对它进行赋值,也不能用“&”运算符取其地址。,45,7.6.2 枚举类型变量的定义,枚举类型变量的定义与前面学习的结构体和共用体变量的定义类似,也有三种形式:(1)先定义枚举类型后定义枚举类型变量其一般形式如下:enum 枚举类型名枚举值列表;enum 类型名 变量名列表;,例如,下面语句enum Color red,green,blue,yellow,white;enum Color change,select;定义了2个枚举类型的变量change和select。,46,(2)枚举类型定义的同时定义变量其一般形式如下:enum 枚举类型名枚举值列表变量名列表;,例如,下面的定义方式enum Color red,green,blue,yellow,whitechange,select;其作用与上例相同。,47,(3)用无名枚举类型定义枚举变量其一般形式如下:enum 枚举值列表变量名列表;,例如:enum red,green,blue,yellow,whitechange,select;,在使用枚举类型变量时,只能将枚举元素赋给枚举变量,不能把枚举值直接赋给枚举变量。例如enum Color red,green,blue,yellow,whitechange,select;change=red;是正确的,而change=0;则是错误的。,48,7.7 自定义数据类型,typedef语句的一般形式如下:typedef 原类型名 新类型名;其中,原类型名必须是C语言提供的标准数据类型或用户自定义的数据类型。例如:typedef float REAL;本例中,定义了一个新数据类型名REAL,它等价于float,此后可以用REAL 来代替float对变量进行定义。,下面举例说明用typedef定义结构体类型别名的方法。typedef struct Student int num;/*学号*/char*name;/*姓名*/char sex3;/*性别*/int age;/*年龄*/float score;/*成绩*/StudentType;,这时,可以用新定义的类型名StudentType来替代结构体类型struct Student,因此,下面的变量定义StudentType person;相当于 struct Student person;而StudentType*pStudent;相当于struct student*pStudent;,49,7.8 综合实例,例 创建一个存放正整数(输入-999做结束标志)的单链表,并输出该链表中的正整数。程序:#include/*包含malloc()函数所在的头文件*/#include struct node/*链表节点的结构*/int num;struct node*next;,struct node*creat(struct node*head)/*函数返回的是与节点相同类型的指针*/struct node*p1,*p2;p1=p2=(struct node*)malloc(sizeof(struct node);/*申请新节点*/scanf(”%d”,/*返回链表的头指针*/,void print(struct node*head)/*输出以head 为头的链表各节点的值*/struct node*temp;temp=head;/*取得链表的头指针*/while(temp!=NULL)/*只要是非空表*/printf(”%6d”,temp-num);/*输出链表节点的值*/temp=temp-next;/*跟踪链表增长*/,int main()struct node*head;/*定义头指针*/head=NULL;/*建一个空表*/head=creat(head);/*创建单链表*/print(head);/*打印单链表*/return 0;,运行结果如下:1 2 3 4 5 6 7-999 1 2 3 4 5 6 7 0,