《第09章结构体、共用体和枚举类型》由会员分享,可在线阅读,更多相关《第09章结构体、共用体和枚举类型(33页珍藏版)》请在金锄头文库上搜索。
1、第第9 9章章 结构体、共用体和枚举类型结构体、共用体和枚举类型9.1 9.1 结构体的定义及应用结构体的定义及应用9.2 9.2 位域位域* *9.3 9.3 共用体的定义及应用共用体的定义及应用9.4 9.4 枚举类型枚举类型9.1 9.1 结构体的定义及应用结构体的定义及应用设每个学生的数据包括:设每个学生的数据包括:姓名:字符型数组姓名:字符型数组年龄:无符号整型年龄:无符号整型成绩:单精度浮点类型成绩:单精度浮点类型 如何存放如何存放1010位学生的原始数据?位学生的原始数据?解:解:定义定义3 3个数组分别存放个数组分别存放1010位学生的姓名、年龄和成绩,位学生的姓名、年龄和成绩
2、,用数组下标区别不同的学生:用数组下标区别不同的学生:char name1020; /char name1020; /存放存放1010位学生的姓名位学生的姓名int age10; / int age10; / 年龄年龄float score10; / float score10; / 成绩成绩 用用nameinamei、ageiagei和和scoreiscorei表示第表示第i i个学生数据。个学生数据。不足:变量多、未直接反映同一学生数据间的联系。不足:变量多、未直接反映同一学生数据间的联系。改进:改进: 定义结构体类型,描述同一学生的多个数据:定义结构体类型,描述同一学生的多个数据: st
3、ruct struct studentstudent /反映同一学生数据间的联系反映同一学生数据间的联系 char name20; / char name20; /姓名姓名 int age; / int age; /年龄年龄 float score; / float score; /成绩成绩 ; ; 定义定义studentstudent类型的数组,存放类型的数组,存放1010位学生的数据:位学生的数据: student s10; /student s10; /变量少变量少结构体可将不同类型的数据组织为一个整体。结构体可将不同类型的数据组织为一个整体。9.1.1 9.1.1 结构体类型的定义结构
4、体类型的定义结构体类型需先定义后使用,其定义格式:结构体类型需先定义后使用,其定义格式: struct struct 结构体类型名结构体类型名 类型类型1 1 成员成员1;1; 类型类型2 2 成员成员2; 2; ; ;说明说明结构体类型名用标识符表示。结构体类型名用标识符表示。成员可以是基本类型或导出类型的变量,不成员可以是基本类型或导出类型的变量,不能指定存储类型为能指定存储类型为autoauto、registerregister、externextern,但可指定存储类型为但可指定存储类型为staticstatic。定义结构体类型用分号定义结构体类型用分号“;”“;”表示结束。表示结束。
5、结构体类型是一个存储模型,本身不占内存,结构体类型是一个存储模型,本身不占内存,仅当定义其变量时,系统才按此存储模型为仅当定义其变量时,系统才按此存储模型为其变量分配相应的内存。其变量分配相应的内存。9.1.2 9.1.2 结构体类型变量的定义结构体类型变量的定义定义结构体类型变量的格式为:定义结构体类型变量的格式为: 结构体类型名结构体类型名 变量列表变量列表; ;或或 struct struct 结构体类型名结构体类型名 变量列表变量列表; ; 其中,结构体类型名为已定义的类型;多个变量之间其中,结构体类型名为已定义的类型;多个变量之间用逗号分隔;后一种格式与用逗号分隔;后一种格式与C C
6、兼容。兼容。例如:例如: student s1,s2,s310;student s1,s2,s310;或或 struct student s1,s2,s310;struct student s1,s2,s310;studentstudent类型变量类型变量s1s1的内存分配图的内存分配图按按studentstudent类型的存储类型的存储模型为其变量模型为其变量s1s1分配内分配内存。存。studentstudent类型的存储模类型的存储模型所需内存字节数:型所需内存字节数: sizeof(student)sizeof(student)s1s1变量各成员在内存中变量各成员在内存中的顺序与其类型
7、中成员的顺序与其类型中成员说明的顺序一致。说明的顺序一致。name占占20字节字节age占占4 4字节字节score占占4 4字节字节结构体类型变量定义的其他形式结构体类型变量定义的其他形式结构体类型变量也可在声明结构体类型的同时定义。结构体类型变量也可在声明结构体类型的同时定义。例如:例如: struct Datestruct Date int year,month,day; int year,month,day; today,yesterdaytoday,yesterday; ;再如:再如: struct / struct /定义一个无名结构类型定义一个无名结构类型 int a; int
8、a; float b; float b; x,yx,y; ; 无名结构体类型不能在该类型定义之外定义其变量。无名结构体类型不能在该类型定义之外定义其变量。结构体类型变量的初始化结构体类型变量的初始化结构体变量的初始化:与数组的初始化方式类似,在结构体变量的初始化:与数组的初始化方式类似,在花括号中,按结构体成员说明的顺序依次列出其值。花括号中,按结构体成员说明的顺序依次列出其值。例如:例如: student s1 student s1=Jenny,20,98=Jenny,20,98; ; Date today Date today=2007,1,2=2007,1,2; ; struct Com
9、plexstruct Complex float real,image; float real,image; c1 c1=2.0f,3.0f=2.0f,3.0f,c2;,c2;9.1.3 9.1.3 结构体类型变量的使用结构体类型变量的使用访问结构体变量成员的格式为:访问结构体变量成员的格式为: 结构体变量结构体变量. .成员名成员名 其中其中“.”“.”是成员访问运算符。例如:是成员访问运算符。例如: today.yeartoday.year 表示访问结构体变量表示访问结构体变量todaytoday的成员的成员yearyear。相同结构体类型的变量之间可直接赋值。例如:相同结构体类型的变量之
10、间可直接赋值。例如: struct weatherstruct weather double temp,wind; double temp,wind; w1=7.5,3.2, w1=7.5,3.2,w2=w1w2=w1; ; 其中其中“w2=w1;”w2=w1;”等同于:等同于: w2.temp=w1.temp; w2.wind=w1.wind;w2.temp=w1.temp; w2.wind=w1.wind;结构体类型变量不能直接输入结构体类型变量不能直接输入/ /输出,其成员能否直接输出,其成员能否直接输入输入/ /输出,取决于其成员的类型,若是基本类型或字输出,取决于其成员的类型,若是基
11、本类型或字符数组,则可直接输入符数组,则可直接输入/ /输出。如:输出。如: cins1; /cins1; /错误错误 cins1.names1.ages1.score; /cins1.names1.ages1.score; /正确正确 couts1; /couts1; /错误错误 couts1.names1.ages1.score;/couts1.names1.ages1.score;/正确正确结构体类型变量做函数的形参时,调用函数的实参必结构体类型变量做函数的形参时,调用函数的实参必须是相同结构体类型的变量。参数的传递方式为值传须是相同结构体类型的变量。参数的传递方式为值传递,系统将实参的
12、每个成员逐个拷贝给对应的形参成递,系统将实参的每个成员逐个拷贝给对应的形参成员。结构体类型变量也可做函数的返回值。员。结构体类型变量也可做函数的返回值。例例9.1 9.1 编写求两分数之和的函数,并用它求编写求两分数之和的函数,并用它求1/8+5/241/8+5/24。 # #includeinclude struct Fraction struct Fraction int num,den; / int num,den; /分别为分数的分子和分母分别为分数的分子和分母 ; ; int gcd(int m,int n)/int gcd(int m,int n)/求求m m和和n n的最大公约数
13、的最大公约数 int t;int t; if(mn) t=m;m=n;n=t; if(mn) t=m;m=n;n=t; while(t=m%n) m=n;n=t; while(t=m%n) m=n;n=t; return n; return n; FractionFraction add( add(Fraction x,Fraction yFraction x,Fraction y)/)/求分数和求分数和 Fraction z;Fraction z; z.num=x.num*y.den+y.num*x.den;/ z.num=x.num*y.den+y.num*x.den;/求分子求分子 z.
14、den=x.den*y.den; /z.den=x.den*y.den; /求分母求分母 int a=gcd(z.num,z.den);int a=gcd(z.num,z.den); z.num/=a; z.den/=a; /z.num/=a; z.den/=a; /约分化简约分化简 return z return z; ; void main() void main() Fraction x=1,8,y=5,24,z; Fraction x=1,8,y=5,24,z; z=z=add(x,y)add(x,y); /; /调用函数求分数之和调用函数求分数之和 coutx+y=z.num/z.d
15、enendl;coutx+y=z.num/z.denendl; 例例9.2 9.2 电视台举办青年歌手大奖赛,请输入每位选手电视台举办青年歌手大奖赛,请输入每位选手的姓名、编号及演唱得分、综合素质得分、新歌得分的姓名、编号及演唱得分、综合素质得分、新歌得分(演唱新歌加(演唱新歌加0.30.3分),计算每人的总分,然后输出每分),计算每人的总分,然后输出每人的全部信息。人的全部信息。 # #includeinclude struct Singer/ struct Singer/定义结构体类型,描述青年歌手定义结构体类型,描述青年歌手 char name10; /char name10; /姓名姓
16、名 char no8; / char no8; /编号编号 float singScore, / float singScore, /演唱得分演唱得分 diaScore, / diaScore, /综合素质得分综合素质得分 newScore; / newScore; /新歌得分新歌得分 float sum; / float sum; /总分总分 ; ; const int n=3; / const int n=3; /参赛青年歌手人数参赛青年歌手人数 void main() void main() Singer snSinger sn; /; /定义结构体数组定义结构体数组 for(int i
17、=0;in;i+)for(int i=0;in;i+) cout cout输入第输入第i+1i+1 “ si.namesi.nosi.singScore cinsi.namesi.nosi.singScore si.diaScoresi.newScore; si.diaScoresi.newScore; si.sum=si.singScore+si.diaScore si.sum=si.singScore+si.diaScore +si.newScore; / +si.newScore; /求总分求总分 for(i=0;in;i+)for(i=0;in;i+) coutsi.nametsi.n
18、ot coutsi.nametsi.not si.singScoretsi.diaScoret si.singScoretsi.diaScoret si.newScoretsi.sumendl; si.newScoretsi.sumendl; 9.2 9.2 位域位域* *内存浪费现象比比皆是。例如:内存浪费现象比比皆是。例如:人的性别只需人的性别只需1 1个二进制位就可精确表示,但通个二进制位就可精确表示,但通常用字符型或字符型数组表示。常用字符型或字符型数组表示。人的年龄只需人的年龄只需7 7个二进制位就可精确表示,但通个二进制位就可精确表示,但通常用整型变量表示。常用整型变量表示。针对上
19、述情况,如何节约使用内存?针对上述情况,如何节约使用内存?位域:结构体中位域:结构体中指定了存指定了存储位数的成位数的成员称称为位域位域。使使用位域可解决上述问题。用位域可解决上述问题。位域类型:位域类型:含有位域的含有位域的结构体构体类型型。定义位域类型定义位域类型格式为:格式为: struct struct 位位域域类型名类型名 类型名类型名 位域名位域名1 :1 :二进制位数二进制位数; /; /定义位域定义位域 类型名类型名 位域名位域名2 :2 :二进制位数二进制位数; /; /定义位域定义位域 / /定义其它成员定义其它成员 ; ; 其中,位其中,位域域类型名用标识符表示,类型名只
20、能是整型类型名用标识符表示,类型名只能是整型和字符型,二进制位数必须是大于或等于和字符型,二进制位数必须是大于或等于0 0的整数。的整数。例如:例如: struct datastruct data unsigned short flaga:1; / unsigned short flaga:1; /取值取值0 0或或1 1 unsigned short flagb:3; / unsigned short flagb:3; /取值取值0 07 7 unsigned short flagc:4; / unsigned short flagc:4; /取值取值0 01515 short flagd:
21、4; / short flagd:4; /取值取值-8-87 7 f1; f1;位位域域类型类型datadata的变量的变量f1f1的内存分配图的内存分配图15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 015 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0注:同一字中位域的分配方向因系统而异,可能从右到左,注:同一字中位域的分配方向因系统而异,可能从右到左,也可能从左到右。也可能从左到右。VC+VC+从右到左。从右到左。若要跳过几个二进位,可定义无名位域。若无名位域若要跳过几个二进位,可定义无名位域。若无名位域的位数为的位数为0 0,则表示下一位
22、域从新单元开始。如:,则表示下一位域从新单元开始。如: struct PackedDatastruct PackedData unsigned flaga:4; unsigned flaga:4; unsigned:2; / unsigned:2; /跳过跳过2 2个二进位,预留个二进位,预留 unsigned:0; /unsigned:0; /下一位域从新单元开始下一位域从新单元开始 unsigned flagd:7; unsigned flagd:7; ; ; flagdflagd位域从下一个位域从下一个unsignedunsigned存存储单元开始存元开始存储。这样这样PackedDat
23、aPackedData类型需要类型需要2 2个个unsignedunsigned存储单元。存储单元。位域类型变量的定义、初始化和使用位域类型变量的定义、初始化和使用位域类型变量的定义、初始化和用法与结构体类型变位域类型变量的定义、初始化和用法与结构体类型变量相同。例如:量相同。例如: data f2=0,4,10,-1;data f2=0,4,10,-1; f2.flagc=12;f2.flagc=12;注意:位域赋值时,若所赋数值超出位域的表示范围,注意:位域赋值时,若所赋数值超出位域的表示范围,则取其低位数字。例如:则取其低位数字。例如: f2.flagb=10;f2.flagb=10;
24、因因1010已超出已超出flagbflagb位域位域0 07 7的表示范围,故赋给的表示范围,故赋给flagbflagb位域的是位域的是1010的二进制表示的低三位:的二进制表示的低三位: 10 10的二进制为的二进制为 1 1 0 1 00 1 0 即即f2.flagbf2.flagb实际为实际为2 2。位域变量节省内存,但存取费时。使用时注意权衡。位域变量节省内存,但存取费时。使用时注意权衡。例例9.3 9.3 用位域来存放学生的信息。用位域来存放学生的信息。# #includeincludeconst int num=2;const int num=2;struct studentstr
25、uct student char name10; / char name10; /姓名:非位域成员姓名:非位域成员 unsigned short sex:1; /unsigned short sex:1; /性别:位域成员性别:位域成员 unsigned short age:7; /unsigned short age:7; /年龄:位域成员年龄:位域成员 unsigned short score:7;/unsigned short score:7;/成绩:位域成员成绩:位域成员;void main()void main() coutsizeof(student)=sizeof(student
26、)endl; coutsizeof(student)=sizeof(student)endl; student snum=Tonny,1,19,98,Jenny,0,18,85; student snum=Tonny,1,19,98,Jenny,0,18,85; for(int i=0;inum;i+) for(int i=0;inum;i+) coutsi.name,si.sex, coutsi.name,si.sex, si.age,si.scoreendl; si.age,si.scoreendl; 9.3 9.3 共用体的定义及应用共用体的定义及应用有时希望多个变量共用同一有时希望多个
27、变量共用同一块内存,可通过定义共用体块内存,可通过定义共用体类型的变量来实现。类型的变量来实现。共用体类型的定义格式:共用体类型的定义格式: union union 共用体类型名共用体类型名 数据类型数据类型 成员名成员名1;1; 数据类型数据类型 成员名成员名2;2; ; ;在定义和使用形式上,共在定义和使用形式上,共用体与结构体相似:由多用体与结构体相似:由多个成员组成,成员的类型个成员组成,成员的类型可以不同。可以不同。在内存分配上,共用体与在内存分配上,共用体与结构体有本质区别:结构结构体有本质区别:结构体的每个成员都有自己的体的每个成员都有自己的独占内存;而共用体的每独占内存;而共用
28、体的每个成员共用同一块内存。个成员共用同一块内存。共同体存储区域的大小由共同体存储区域的大小由占用最大存储区的成员决占用最大存储区的成员决定。定。例如:例如: union udataunion udata char c; char c; int i; int i; double d; double d; ; ;共用体类型定义后,即可定义其变量、数组、指针和共用体类型定义后,即可定义其变量、数组、指针和引用等。例如:引用等。例如: udata u1, / udata u1, /变量变量u1u1实际占用实际占用8 8字节内存字节内存 u210,u210, * *p=u2;p=u2;对共用体变量的操
29、作与结构体变量类似。例如:对共用体变量的操作与结构体变量类似。例如: u1.c=a;u1.c=a; u1.i=25; u1.i=25; u1.d=15.2; u1.d=15.2; 注意,当对变量注意,当对变量u1u1的成员的成员i i赋值时,覆盖了成员赋值时,覆盖了成员c c的值;的值;类似的当对成员类似的当对成员d d赋值时,又覆盖了成员赋值时,又覆盖了成员i i的值。同一的值。同一时刻,只可使用其中的一个成员。时刻,只可使用其中的一个成员。共用体可做函数的参数和返回值。共用体可做函数的参数和返回值。应用举例:学生体能测试,男生测应用举例:学生体能测试,男生测100100米,女生测跳绳。米,
30、女生测跳绳。要求输入学生的体能测试数据并输出。要求输入学生的体能测试数据并输出。#include#includeusing namespace std;using namespace std;struct studentstruct studentchar sex; /char sex; /性别:性别: mm为男生,为男生, ff为女生为女生unionunionfloat run; /float run; /百米成绩百米成绩int rope; /int rope; /跳绳成绩跳绳成绩 score;score;void input(student s,int n)void input(stude
31、nt s,int n) for(int i=0;in;i+) for(int i=0;in;i+) cout coutsi.sex;cinsi.sex; if(si.sex=m) if(si.sex=m)coutcoutsi.score.run;cinsi.score.run; else if(si.sex=f) else if(si.sex=f)coutcoutsi.score.rope;cinsi.score.rope; else cout else cout非法数据非法数据 n;n; void output(student s,int n)void output(student s,in
32、t n) for(int i=0;in;i+)for(int i=0;in;i+) if(si.sex=m) if(si.sex=m) coutn coutn男生百米成绩:男生百米成绩:si.score.run;si.score.run; else if(si.sex=f) else if(si.sex=f) coutn coutn女生跳绳成绩:女生跳绳成绩:si.score.rope;si.score.rope; else coutn else coutn非法数据非法数据 n;n; void main(void)void main(void) const int n=3;const int
33、n=3;student sn;student sn;input(s,n);input(s,n);output(s,n);output(s,n); 9.4 9.4 枚举类型枚举类型现实中有些数据只有有限几种取值。例如:现实中有些数据只有有限几种取值。例如:交通灯的颜色:红、黄、绿;交通灯的颜色:红、黄、绿;人的性别:男、女;人的性别:男、女;一个星期:星期一、星期二、一个星期:星期一、星期二、星期日。、星期日。如何表达这些数据?如何表达这些数据?用用intint、charchar类型。例如,类型。例如,charchar型变量表示性别,型变量表示性别,用用mm和和ff分别表示男和女,其它字符则不合
34、分别表示男和女,其它字符则不合法,但编译器无法检查出这类错误。法,但编译器无法检查出这类错误。用枚举类型:可更好解决这类问题。用枚举类型:可更好解决这类问题。枚举类型的定义枚举类型的定义定义枚举类型的格式为:定义枚举类型的格式为: enum enum ; ; 枚举类型名为标识符,枚举量表由逗号隔开的标识符枚举类型名为标识符,枚举量表由逗号隔开的标识符组成。枚举量表中的标识符称为枚举常量。组成。枚举量表中的标识符称为枚举常量。例如:例如: enum weekdaySun,Mon,Tue,Wed,Thu,Fri,Sat;enum weekdaySun,Mon,Tue,Wed,Thu,Fri,Sat
35、;定义枚举类型时,每个枚举常量对应一个整数值。定义枚举类型时,每个枚举常量对应一个整数值。若未指定枚举常量的值,则第若未指定枚举常量的值,则第1 1个枚举常量为个枚举常量为0 0,第,第2 2个枚举个枚举常量为常量为1 1,依次类推。,依次类推。也可给枚举常量指定值。例如:也可给枚举常量指定值。例如: enum weekdaySun=7,Mon=1,Tue,Wed,Thu,Fri,Sat;enum weekdaySun=7,Mon=1,Tue,Wed,Thu,Fri,Sat; 此时此时SunSun为为7 7,MonMon为为1 1,TueTue为为2 2、SatSat为为6 6,即未指定值的,
36、即未指定值的枚举常量,其值为前一枚举常量的值增枚举常量,其值为前一枚举常量的值增1 1。枚举类型变量的定义枚举类型变量的定义枚举变量的定义格式:枚举变量的定义格式: 枚举类型名枚举类型名 变量名变量名; ;或或 enum enum 枚举类型名枚举类型名 变量名变量名; /; /与与C C兼容兼容例如:例如: weekday weekday workday, weekendworkday, weekend; ; 枚举变量枚举变量workdayworkday和和weekendweekend的值只能是的值只能是SunSun到到SatSat之一。之一。也可在定义枚举类型时定义枚举变量。例如:也可在定义
37、枚举类型时定义枚举变量。例如: enum weekdayenum weekday Sun,Mon,Tue,Wed,Thu,Fri,Sat Sun,Mon,Tue,Wed,Thu,Fri,Sat day1,day2day1,day2; ;枚举类型变量的使用枚举类型变量的使用赋值运算:枚举常量赋给枚举类型变量,或同类型的赋值运算:枚举常量赋给枚举类型变量,或同类型的枚举类型变量之间相互赋值。例如:枚举类型变量之间相互赋值。例如: day1=Sun; day2=day1;day1=Sun; day2=day1; 不能将一个整数直接赋给枚举类型变量。例如:不能将一个整数直接赋给枚举类型变量。例如: e
38、num ColorRed,Green,Yellowc1,c2;enum ColorRed,Green,Yellowc1,c2; c1=Yellow; / c1=Yellow; /正确正确 c2=1; /c2=1; /错误错误 可用强制类型转换把整数赋给枚举类型变量。例如:可用强制类型转换把整数赋给枚举类型变量。例如: c2=(Color)1; / c2=(Color)1; / 或或c2=Color(1);c2=Color(1); 其效果等价于:其效果等价于: c2=Green;c2=Green;枚举类型变量的使用枚举类型变量的使用关系运算:枚举值进行比较时,比较的是它们对应的关系运算:枚举值进
39、行比较时,比较的是它们对应的整型值的大小。例如整型值的大小。例如c1c2c1c2的运算结果为的运算结果为truetrue。输出:直接输出枚举变量时,输出的是枚举变量对应输出:直接输出枚举变量时,输出的是枚举变量对应的整数。如需输出对应的字符串,必须通过代码进行的整数。如需输出对应的字符串,必须通过代码进行转换。转换。输入:不能直接输入枚举类型变量的值,例如:输入:不能直接输入枚举类型变量的值,例如: cinday1; /cinday1; /错误错误 对于枚举值的输入,通常通过输入一个整型值,然后对于枚举值的输入,通常通过输入一个整型值,然后把该整型值转换成一个枚举值。把该整型值转换成一个枚举值
40、。例例9.4 9.4 输入输入/ /输出枚举类型的值。输出枚举类型的值。 # #includeinclude enum Major chinese,math,physics,computer ; enum Major chinese,math,physics,computer ; void main() void main() Major m; Major m; int n; int n; coutInput the major: coutInput the major: 0-chinese,1-math,2-physics,3-computern; n; cinn; switch(n) sw
41、itch(n) case 0: m=chinese;break; case 0: m=chinese;break; case 1: m=math;break; case 1: m=math;break; case 2: m=physics;break; case 2: m=physics;break; case 3: m=computer;break; case 3: m=computer;break; default: coutInput error!n; return; default: coutInput error!n; return; switch(m) switch(m) case chinese: coutChinesen; break; case chinese: coutChinesen; break; case math: coutmathn; break; case math: coutmathn; break; case physics: coutphsicsn; break; case physics: coutphsicsn; break; case computer: coutcomputern; break; case computer: coutcomputern; break;