结构体与共用体22课件_第1页
结构体与共用体22课件_第2页
结构体与共用体22课件_第3页
结构体与共用体22课件_第4页
结构体与共用体22课件_第5页
已阅读5页,还剩91页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

掌握结构体变量的定义、引用和初始化了解结构体数组应用理解指向结构体数据的指针了解共用体的概念、引用方法第十章结构体与共用体第十章结构体与共用体10.1概述迄今为止,已介绍了基本类型(或称简单类型)的变量(如整型、实型、字符型变量等),也介绍了一种构造类型数据——数组,数组中的各元素是属于同一个类型的。但是只有这些数据型是不够的。有时需要将不同类型的数据组合成一个有机的整体,以便于引用。这些组合在一个整体中的数据是互相联系的。例如,一个学生的学号、姓名、性别、年龄、成绩、家庭地址等项。这些项都与某一学生相联系,见图10。1。可以看到性别(sex)、年龄(age)、成绩(score)、地址(addr)是属于学号为10010和名为“LiFun”的学生的。如果将.sex.age.score.addr分别定义为互相独立的简单变量,是难以反映它们之间的内在联系的。应当把它们组织成一个组合项,在一个组合项中包含若干个类型不同(当然也可以相同)的数据项。C语言提供了这样一种数据结构,它称为结构体(structure)。它相当于其它高级语言中的“记录”。numnamesexagescoreaddr10010LiFunM1887.5Beijing上面定义了一个结构体类型,structstudent(struct是关键字,不能省略),表示这是一个“结构体类型”。它包括num,name,age,score,addr等不同类型的数据项。应当注意:structstudent是程序设计者自己定义的类型名。它和系统已定义了的标准类型(如int、char、float、double等)一样可以用来作为定义变量的类型。定义一个结构体类型的一般形式为:

struct结构体名{成员表列};花括弧内是该结构体中的各个成员(或称分量),由它们组成一个结构体。例如,上例中的num,name,sex等都是成员。对各成员都应进行类型说明,即

类型标识符成员名也可以把“成员表列”称为“域表”。

每一个成员称为结构体中的一个域。成员名定名规则与变量名同.“结构体”这个词是根据英文单词structure译出的。许多C语言书把structure直译为“结构”。作者认为译作“结构”会与一般含义上的“结构”混淆(例如,数据结构、程序结构)。日本书刊把structure译作“结构体”或“构造体”,作者认为译作“结构体”比译作‘结构”确切一些,不致与一般含义上的“结构”混淆。$10.2定义结构体类型变量的方法要定义一个结构体类型的变量,可以采取以下三种方法。

一、先定义结构体类型再定义变量名如上面已定义了一个结构体类型structstudent,可以用它来定义变量。如structstudentstudent1,student2;定义student1和student2为structstudent类型变量,即它们具有structstudent类型的结构。如图所示。student110001ZhangXinM1990.Shanghaistudent210002WangLIF2098Beijing应当注意:将一个变量定义为标准类型(基本数据类型)与定义为结构体类型不同之处在于:后者不仅要求指定变量为结构体类型,而且要求指定为某一特定的结构体类型(例如,structstudent),不能只指定为“struct型”而不指定结构体名。可以直接用STUDENT定义变量。如STUDENTstudent1,student2;用这种方法定义变量和用int,float定义变量的形式相仿,不必再写关键字struct.如果程序规模比较大,往往将对结构体类型的定义集中放到一个文件(以·h为后缀的“头文件")中。哪个源文件需用到此结构类型则可用#include命令将该头文件包含到本文件中。这样做便于装配,便于修改,便于使用。

二、在定义类型的同时定义变量例如:structstudent{intnum;charname[2];charsex;intage;floatscore;charaddr[30];}student1,student2;它的作用与前面定义的相同。既定义了两个structstudent类型的变量student1,student2.这种形式的定义的一般形式为:struct结构体名{成员表列}变量名表列;

三、直接定义结构类型变量其一般形式为:struct{成员表列}变量名表列;即不出现结构体名。3.成员也可以是一个结构体变量。如:structdate{intmonth;intday;intyear;};structstudent{intnum;charname[20];charsex;intage;structdatebirthday;charaddr[30];}student1,student2;先定义一个structdate类型,它代表“日期”,包括三个成员:month,day,year然后在定义structstudent类型时,成员birthday的类型定义为structdate类型.structstudent的结构见图10·3所示。已定义的类型structdate与其它类型(如int,char)一样可以用来定义成员的类型birthdaynumnamesexagemonthdayyearaddr

10.3结构体类型变量的引用在定义了结构体变量以后,当然可以引用这个变量。但应遵守以下规则:1·不能将一个结构体变量作为一个整体进行输入和输出。例如,已定义:student1和student2为结构体变量并且它们已有值。不能这样引用:printf(”%d,%s,%c,%d,%f,%s\n,”,student1);只能对结构体变量中的各个成员分别输出。引用方式为:

结构体变量名.成员名例如:student1.num表示student1变量中的num成员,即student1的num(学号)项。可以对变量的成员赋值,例如:student.num=10010;“·”是成员(分量)运算符,它在所有的运算符中优先级最高,因此可以把srudent1.num作为一个整体来看待。3.对成员变量可以像普通变量一样进行各种运算(根据其类型决定可以进行的运算)。例如:student2.score=student1.score;sum=student1.score+student2.score;student1.age++;++student1.age;由于'.”运算符的优先级最高,因此student1.age++是对student1.age进行自加运算,而不是先对age进行自加运算。4.可以引用成员的地址,也可以引用结构体变量的地址。如:scanf("%d”,&student1.num);(输入student1.num的值)printf(”%o”,&student1);(输出student1的首地址)但不能用以下语句整体读入结构体变量,如scanf(“%d,%s,%c,%d,%f,%s”,&student1);结构体变量的地址主要用于作函数参数,传递结构体的地址。

10.4结构体变量的初始化

过去许多C版本规定,只有当结构体变量为全局变量或静态变量时,才能进行初始化。不能对动态局部变量进行初始化。新版本无此限制。一、对外部存储类型的结构体变量进行初始化。[例10.1]structstudent{1ongintnum;charname[20];charsex;charaddr[20];}a={89031,“Lilin”,‘M’,“123beijingRoad”};

二、对静态存储类型的结构体变量进行初始化。上面例10.1的定义部分可以放到main函数中。main(){staticstructstudent{longintnum;charname[20];charsex;charaddr[20];}a={89031,"LiLin",`M',"123BeijingRoad"};printf("No.:%1d\nname:%s\nsex:%c\naddress:%s\n",a.num,,a.sex,a.addr);}三、过去许多C版本规定,自动变量不能在定义时赋初值,只能在函数执行时用赋值语句对各成员分别赋值。新的C标准无此限制,允许对自动变量初始化。

10.5结构体数组一个结构体变量中可以存放一组数据(如一个学生的学号、姓名、成绩……等数据)。如果有10个学生的数据需要参加运算,显然应该用数组,这就是结构体数组。结构体数组与以前介绍过的数值型数组不同之处在于每个数组元素都是一个结构体类型的数据,它们都分别包括各个成员(分量)项。

一、结构体数组的定义和定义结构体变量的方法相仿,只需说明其为数组即可。如:structstudent{intnum;charname「20]charsex;intage;floatscore:charaddr[30];};structstudentstu[3];以上定义了一个数组stu,其元素为structstudent类型数据,数组有3个元素。也可以直接定义一个结构体数组,如:二、结构体数组的初始化结构体数组可以初始化。如:structstudent{intnum;charname「20];charsex;intage;floatscore;charadd[30];}stu[3]={{10101,"LiLin”,'M',18,87.5,"103BeijingRoad"},{10102,“ZhangFun”,’M’,19,99,”130ShanghaiRoad},{10104,“WangMin”,'F',20,78.5,”1010ZhongshanRoad}};定义数组stu时,元素个数可以不指定,即谢成以下形式:stu[]={{…},{…},{…}};编译时,系统会根据给出初值的结构体常量的个数来确定数组元素的个数。当然,数组的初始化也可以用以下形式:structstudent{intnum

};structstudentstu[]={{···},{···},{···}};即先定义结构体类型,然后定义数组为该结构体类型,在定义数组时初始化。从以上可以看到,结构体数组初始化的一般形式是在定义数组的后面加上:={初值表列};运行情况如下:LiLiFunZhangZhangFunLiFunZhangLi

Li:4Zhang:3Fun:3程序定义一个全局的结构体数组1eader,它有3个元素,每一元素包含两个成员name(姓名)和count(票数)。在定义数组时使之初始化,使三位候选人的票数都先置零。在主函数中定义字符数组1eader_name,它代表被选人的姓名,在10次循环中每次先输入一个被选入的具体人名,然后把它与三个候选人姓名相比,看它和哪一个候选人的名字相同。注意leader_name是和leader[j].name相比,leader[j]是数组leader的第j个元素,由于它包含两个成员项,所以leader_name应该和leader数组第j个元素的name成员相比。当j为某一值时,输入的姓名与leader[j].name相等,就使该元素的成员count的值加1,即leader[j].count++,由于成员运算符”.”优先于自增运算符”++”,因此它相当于(leader[j].count)++。在输入和统计结束之后,将三人的名字和得票数输出。

10.6指向结构体类型数据的指针一个结构体变量的指针就是该变量所占据的内存段的起始地址。可以设一个指针变量,用来指向一个结构体变量,此时该指针变量的值是结构体变量的起始地址。指针变量也可以用来指向结构体数组中的元素。10.6.1指向结构体变量的指针下面通过一个简单例子来说明指向结构体变量的指针变量的应用。[例10.3]#include"string.h"main(){structstudent{longintnum;charname[20];charsex;floatscore;};structstudentstu_1;structstudent*p;p=&stu_1;stu_1.num=89101;strcpy(stu_1.name,”Lilin”);stu_1.sex=’M’;stu_1.score=89.5;printf(”No,:%ld\nname:%s\nsex:%c\nscore:%f\n”,stu_1.num,stu_1.name,stu_1.sex,stu_1.score);printf("\nNo.:%1d\nname:%s\nsex:%c\nscore:%f\n",(*p).num,(*p).name,(*.p).sex,(*p).score);}p89101“Lilin”‘M’89.5在主函数中定structstudent类型,然后定义一个structstudent类型的变量stu_1.同时又定义一个指针变量p,它指向一个structstudent类型的数据。在函数的执行部分stu_1的起始地址赋给p,也就是使p指向,stu_1(见图10.7),然后对,stu_1的各成员赋值。第一个printf是输出函数stu_1的各个成员的值。用stu_1.num表示stu_1中的成员num,余类推。第二个printf函数也是用来输出stu_1各成员的值,但使用的是(*p).num这样的形式。(*p)表示p指向的结构体变量,(*p).num是p指向的结构体变量中的成员num。注意*p的括弧不可省,因为成员运算符"."优先于”*”运算符;*p.num就等价于*(p.num)了.程序运行结果如下(双栏排):No.:89101No.:89101name:LiLinname:LiLinsex:Msex:Mscore:89.500000score:89.50000可见两个printf函数输出的结果是相同的。在C语言中,为了使用方便和使之直观,可以把(*p).num改用p->num来代替,即:p所指向的结构体变量中的num成员。同样,(*p).name等价于p->name。也就是说,以下三种形式等价:①结构体变量.成员名;②(*p).成员名;③p->成员名上面程序中最后一个printf函数中的输出项表列可以改写为:p->num,p->name,p->sex,p->score其请分析以下几种运算:p->n得到p指向的结构体变量中的成员n的值。p->n++得到p指向的结构体变量中的成员n的值,用完该值后使它加1。++p->n得到p指向的结构体变量中的成员n的值使之加1(先加)。

10.6.2指向结构体数组的指针以前已经介绍过,可以使用指向数组或数组元素的指针和指针变量。同样,对结构体数组及其元素也可以用指针或指针变量来指向。[例10.4]structstudent{intnum;charname「20];charsex;intage;};structstudentstu[3]={{10101,”LiLin”,‘M’,18},{10102,”ZhangFun”,’M‘,19},{10104,"wangMin",`F`,20}};

main(){structstudent*p;printf(”No.Namesexage\n”);for(p=stu;p<stu+3;p++)printf(”%5d%-20s%2c%4d\n”,p->num,p->name,p->sex,p->age);}运行结果如下:No.Namesexage->称为指向运算符。10101LiLinM1810102MangFunM1910104WangMinF20p是指向structstudent结构体类型数据的指针变量。在for语句中先使p的初值为stu,也就是数组stu的起始地址,见图10.8中p的指向。在第一次循环中输出stu[0]的各个成员值。然后执行p++,使p自加1。p+1意味着增加的地址值为结构体类型数组stu的一个元素所占的字节数(在本例中为2+20+1+2=25字节),p十十使p指向stu[1]的起始地址,见图10.8中p'的指向。在第二次循环中输出stu[1]的各成员值。在执行p+十后,p的值等于stu+2,它的指向见图10.8中的p''.再输出stu[2]的各成员值。在执行p++后,p的值变为stu+3,已不再小于stu+3了,不再执行循环。注意以下两点:1.如果p的初值为stu,即指向第一个元素,则p+1后指向下一个元素的起始地址。例如:(++p)一>num先使p自加1,然后得到它指向的元素中的num成员值(即10102)。(p++)->num先得到p一>num的值(即10101),然后使请注意以上二者的不同。2.指针p已定义为指向structstudent类型的数据,它只能指向一个结构体型数据(也就是p的值是stu数组的一个元素的起始地址),而不能指向一元素中的某一成员(即p的地址不能是成员的地址),例如,下面是不对的:p=&stu.name千万不要认为:反正p是存放地址的,可以将任何地址赋给它。如果地址类型不相同,可以用强制类型转换。例如:p=(structstudent*)&stu.name:

10.6.3用指向结构体的指针作函数参数有时想将一个结构体变量的值传递给另一个函数,但原来的C标准不允许用结构体变量作为函数参数。那么用什么方法来解决这个问题呢?有两个方法:①用结构体变量的成员作参数。例如,用stu[1].num或stu[2].name作函数实参,将实参值传给形参。用法和用普通变量作实参是一样的,属“值传递”方式。②用指向结构体变量(或数组)的指针作实参,将结构体变量(或数组)的地址传给形参。「例10·5】有一个结构体变量stu,内含学生学号、姓名和三门课的成绩,要求在main函数中赋以值,在另一函数print中将它们打印输出。#include"string.h"#defineformat“%d\n%s\n%f\n%f\n%f\n”structstudent{intnum;charname[20];floatscore[3];};main()p{voidprint()structstudentstu;stu.num=12345;strcpy(stu.name,”LiLi”);stu.score[0]=67.5stu.score[1]=89;stu.score[2]=78.6;print(&stu);}voidprint(p)structstudent*p;{printf(format,p->num,p->name,p->score[0],p->score[1],p->score[2]);prinif(”\n");}

numnameScore[0]Score[1]Score[2]运行结果为:12345LiLi67.50000089.00000078.599998structstudent被定义为外部类型,这样,同一源文件中的各个函数都可以用它来定义变量的类型。main函数中的stu变量定义为structstudent类型,print函数中的形参p被定义为指向structstudent类型数据的指针变量。在main函数中对stu的各成员赋值。注意在调用print函数时,用&stu作实参,&stu是结构体变量stu的地址。在调用函数时将该地址传送给形参p(p是指针变量)。这样p就指向了stu,见图10.9。在print函数中输出p所指向的结构体变量的各个成员值,它们也就是stu的成员值。main函数中的对各成员赋值也可以改用scanf函数输入。即用scanf(”%d%s%f%f%f",&stu.num,stu.name,&stu.score[0],&stu.score[1],&stu.score[2]);输入时用下面形式输入:12345Li_Li67.58978.6注意:输入项表列中stu.name。前没有&符号,因为stu.name是字符数组名本身代表地址,不应写成&stu.name.ANSIC允许用整个结构体作为函数的参数传递,但是必须保证实参与形参的类型相同。例10.5中main函数中最后一行调用print函数,也可改用print(stu);即实参改用结构体变量(而不是指针)。同时print函数也应相应改为:voidprint(stud)structstudentstud:{printf(format,stud.num,stud.name,stud.score[0],stud.score[1],stud.score[2]);printf(”\n”);}把一个完整的结构体变量作为参数传递,虽然合法,但要将全部成员值一个一个传递,费时间又费空间,开销大。如果结构体类型中的成员很多,或有一些成员是数组,则程序运行效率会大大降低。在这种情况下,用指针作函数参数比较好,能提高运行效率。10.6.4举例[例10.6]有4个学生,每个学生包括学号、姓名、成绩。要求找出成绩最高者的姓名和成绩。程序如下:

main(){structstudent{intnum;charname[20];floatscore;};structstudentstu[4];structstudent*pinti,temp=0;floatmax;for(i=0,i<4:i++)scanf(”%d%s%f”,&stu[i].num,stu[i].name,&stu[i].score);

for(max=stu[0].score,i=1;i<4;i++)if(stu[i].score>max){max=stu[i].score;temp=i;}p=stu+temp;printf(“\nThemaxscore:\n");printf(“No.:%d\nname:%s\nscore:%4.1f\n",p->num,p->name,p->score);}

运行情况如下:101Li90102Tan98103Wang67104Fun89Themaxscore:No:102name:Tanscore:98.0程序中定义stu为structstudent类型的数组。p为指向structstudent类型数据的指针变量。第一个for循环输入4个学生的数据。在第二个for循环中,先使变量max的值等于stu[0].score(即第0个学生的成绩),然后用max与其余3个学生的成绩(即stu[i].score)相比,若后者大于max,则将大者放人max,并记下此时i的值(将i的值存放到temp保存起来)。接着使p=stu十temp,也就是将p定位于成绩最高者的那个数组元素处(见图10.10)。最后输出p所指的元素中各个成员值。

10.8共用体共用体的概念有时需要使几种不同类型的变量存放到同一段内存单元中。例如,可把一个整型变量、一个字符型变量、一个实型变量放在同一个地址开始的内存单元中(见图10.25)。以上三个变量在内存中占的字节数不同,但都从同一地址开始(图中设地址为1000)存放。1000ichf也就是使用覆盖技术,几个变量互相覆盖。这种使几个不同的变量共占同一段内存的结构,称为“共用体”类型的结构。“共用体”类型变量的定义形式为:union共用体名{成员表列;}变量表列;例如:uniondata{inti;charch;floatf;}a,b,c;也可以将类型定义与变量定义分开:uniondata{inti;charch;floatf;};uniondataa,b,c;即先定义一个uniondata类型,再将abc定义为uniondata类型。当然也可以直接定义共用体变量,如:union{inti;charf;}a,b,c;可以看到,“共用体”与“结构体”的定义形式相似。但它们的含义是不同的。结构体变量所占内存长度是各成员占的内存长度之和。每个成员分别占有其自己的内存单元。共用体变量所占的内存长度等于最长的成员的长度。例如,上面定义的“共用体"变量a,b,c各占4个字节(因为一个实型变量占4个字节),而不是各占2+1+4=7个字节。

目前国内有关C语言的书多把uniOn直译为“联合”。作者认为,译为“共用体”更能反映这种结构的特点,即几个变量共用一个内存区。而“联合”这一名词;在一般意义上容易被理解为“将两个或若干个变量联结在一起”,难以表达这种结构的特点。日本就是用“共用体"这一术语的。10.8.2共用体变量的引用方式只有先定义了共用体变量才能引用它。而且不能引用共用体变量,而只能引用共用体变量中的成员。例如,前面定义了a,b,c为共用体变量,下面的引用方式是正确的:a.i(引用共用体变量中的整型变量i)a.ch(引用共用体变量中的字符变量ch)a.f(引用共用体变量中的实型变量f)不能只引用共用体变量,例如:printf("%d",a)是错误的,a的存储区有好几种类型,分别占不同长度的存储区,仅写共用体变量名a,难以使系统确定究竟输出的是哪一个成员的值。应该写成prinif(”%d,a.i)或printf(”%c",a.ch)等10.8.3共用体类型数据的特点在使用共用体类型数据时要注意以下一些特点:1.同一个内存段可以用来存放几种不同类型的成员,但在每一瞬时只能存放其中一种,而不是同时存放儿种。也就是说,每一瞬时只有一个成员起作用,其它的成员不起作用,即不是同时都存在和起作用。2.共用体变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员就失去作用。如有以下赋值语句)a.i=1;a.ch='a';a.f=1.5;在完成以上三个赋值运算以后,只有a.f是有效的,a.i和a.c已经无意义了。此时用printf(”%d“,a.i)是不行的,而用printf(”%f”,a.f)是可以的,因为最后一次的赋值是向a.f赋值。因此在引用共用体变量时应十分注意当前存放在共用体变量中的究竟是哪个成员。3.共用体变量的地址和它的各成员的地址都是同一地址。例如:&a.&a.i&a.ch.&a.f都是同一地址值,其原因是显然的。4.不能对共用体变量名赋值,也不能企图引用变量名来得到成员的值,又不能在定义共用体变量时对它初始化。例如,下面这些都是不对的:①union{inti;charch;floatf;}a={1,‘a’,1.5};(不能初始化)②a=1;(不能对共用体变量赋值)③m=a;(不能引用共用体变量名以得到值)5.不能把共用体变量作为函数参数,也不能使函数带回共用体变量,但可以使用指向共用体变量的指针(与结构体变量这种用法相仿)。6.共用体类型可以出现在结构体类型定义中,也可以定义共用体数组。反之,结构体也可以出现在共用体类型定义中,数组也可以作为共用体的成员。「例10.11]设有若干个人员的数据,其中有学生和教师。学生的数据中包括:姓名、号码、性别、职业、班级。教师的数据包括:姓名、号码、性别、职业、职务。可以看出,学生和教师所包含的数据是不同的。现要求把它们放在同一表格中,见图10.26。如果“job”项为“s”(学生),则第5项为class(班)。即Li是501班的。如果“job”项是“T”(教师),则第5项为position(职务)。wang是prof(教授)。显然对第5项可以用共用体来处理(将class和pOsiti0n放在同一段内存中)。要求输入人员的数据,然后再输出。可以写出下面的算法(见图10.27)。按此写出程序。为简化起见。只设两个人(一个学生、一个教师)/*example10.11atpage244*/#defineN2struct{intnum;charname[10];charsex;charjob;union{intclass; charposition[10];}category;}person[2];

main(){intn,i;for(i=0;i<N;i++){printf("Inuptthenumnamesexandjob(sort)\n");scanf("%d%s%c%c", &person[i].num,person[i].name,&person[i].sex,&person[i].job); if(person[i].job=='s') {printf("inputclassnum\n"); scanf("%d",&person[i].category.class);} elseif(person[i].job=='t') {printf("inputposition\n"); scanf("%s",person[i].category.position);} elseprintf("inputerror!");}

printf("\n"); printf("Nsexjobclass/position\n"); for(i=0;i<N;i++) {if(person[i].job=='s') printf("%-6d%-10s%-3c%-3c%-10d\n",person[i].num, person[i].name,person[i].sex,person[i].job, person[i].category.class); elseprintf("%-6d%-10s%-3c%-3c%-10s\n",person[i].num, person[i].name,person[i].sex,person[i].job, person[i].category.position); }}运行情况如下:101Lifs501102WangmtprofessorNo。Namesexjobclass/position

可以看到:在main函数之前定义了外部的结构体数组person,在结构体类型定义中包括了共用体类型,category(分类)是结构体中一个成员名,在这个共体中一个成员为class和position,前者为整型,后者为字符数组(存放“职务”的值——字符串)。这种共用体变量的用法是很有用的。例如,可以设一个单元data,内放一个常量,此常量可以是整型、实型或字符型、双精度等。在程序中根据“类型标志”来决定按何种类型处理。如:struct{...union{intI;charch;floatf;doubled;}data;inttype;)a;switch(a.type)case0:printf(“%d\n”,a.data.i);break;Case1:printf(”%c\n",a.data.ch);break;Case2:printf(”%f\n”a.data.f);break;Case3:printf(”%f\n”,a.data.d);break;}结构体中的type作为“类型标志”,如果在共用体成员中存放整数,则使type=0;若存放字符,则使type=1;若存放实型,则使typ=2;若存放双精度型,使type=3。然后在switch语句中根据a.type的值来决定按哪一种类型输出。这在写系统软件时,用来处理符号表是有用的。在符号表中可以包含符号名、类型和值。根据不同情况进行不同处理。

10.9枚举类型枚举类型是ANSIC新标准所增加的。如果一个变量只有几种可能的值,可以定义为枚举类型。所谓“枚举”是指将变量的值一一列举出来,变量的值只限于列举出来的值的范围内。定义枚举类型用enum(enumerate)开头。例如enumweekday{sun,mon,tue,wed,thu,fri,sat};定义了一个枚举类型enumweekday,可以用此类型来定义变量。如enumweekdayworkday,week_end;workday和week_end被定义为枚举变量,它们的值只能是sun到sat之一。例如:workday=mon;week_end=sun;是正确的。当然,也可以直接定义枚举变量,如

enumweekday{sun,mon,tue,wed,thu,fri,sat}workday,week_end

;其中sun、mon、…、sat、等称为枚举元素或枚举常量。它们是用户定义的标识符。这些标识符并不自动地代表什么含义。例如,不因为写成sun,就自动代表“星期天".不写sun而写成sunday也可以。用什么标识符代表什么含义,完全由程序员决定,并在程序中作相应处理。说明1·在C编译中,对枚举元素按常量处理,故称枚举常量。它们不是变量,不能对它们赋值。例如sun=0;mon=1;是错误的。2.枚举元素作为常量,它们是有值的,C语言编译按定义时的顺序使它们的值为0,1,2,……。在上面定义中,sun的值为0,mon的值为1,sat为6。如果有赋值语句workday=mon;workday变量的值为1。这个整数是可以输出的。如printf(”%d”,workday);将输出整数1。也可以改变枚举元素的值,在定义时由程序员指定,如enumweekday{sun=7,mon=1,tue,wed,thu,fri,sat}workday,week_end;定义sun为7,mon=1,以后顺序加1,sat为6.3.枚举值可以用来作判断比较。如if(workday==mon)…if(workday>sun)···枚举值的比较规则是:按其在定义时的顺序号比较。如果定义时未人为指定,则第一个枚举元素的值认作0。故mon大于sun,sat>fri。4.一个整数不能直接赋给一个枚举变量。如workday=2;是不对的。它们属于不同的类型。应先进行强制类型转换才能赋值。如workday=(enumweekday)2;它相当干将顺序号为2的枚举元素赋给workday,相当于workday=tue;甚至可以是表达式。如workday=(enumweekday)(5-3);[例10.12】口袋中有红、黄、蓝、白、黑五种颜色的球若干个。每次从口袋中取出3个球,问得到三种不同色的球的可能取法,打印出每种组合的三种颜色。球只能是5种色之一,而且要判断各球是否同色,应该用枚举类型变量处理。设取出的球为I,j,k.根据题意,I,j,k分别是5种色球之一,并要求I!=j!=k.可以用穷举法,即一种可能一种可能地试,看哪一组符合条件。算法可用图10。28表示。用n累计得到三种不同色球的次数。外循环使第一个球i从red变到black。中循环使第二个球j也从red变到black。如果i和j同色则不可取;只有I,j不同色(I!=j)时才需要继续找第三个球,此时第三个球k也有5种可能(red到black),但要求第三个球不能与第一个球或第二个球同色,即k!=I,k!=j.满足此条件就得到三种不同色的球。输出这种三色组合方案。然后使n加1。外循环全部执行完后,全部方案就已输出完了。最后输出总数n.下面的问题是如何实现图10.28中的“输出一种取法”。这里有一个问题:如何输出“red”、“blue”、……等单词。不能写成printf("%s",red)来输出“red”字符串。可以采用图10.29的方法。为了输出3个球的颜色,显然应经过三次循环,第一次输出i的颜色,第二次输出j的颜色,第三次输出k的颜色。在三次循环中先后将I,j,k赋予pri。然后根据pri的值输出颜色信息。在第一次循环时,pri的值为1,如果i的值为red,则输出字符串“red”,其它的类推。程序如下:main(){enumcolor{red,yellow,blue,white,black};enumcolori,j,k,pri;intn,loop;n=0;for(I=red;I<=black;I++)for(j=red;j<=balack;j++)if(I!=j){for(k=red;k<=black,k++)if((k!=I)&&(k!=j)){n=n+1;printf("%-4d",n);for(loop=1;loop<=3;loop++){switch(loop){case1:pri=i;break;case2:pri=j;break;case3:pri=k;break;default:break;}switch(pri){casered:printf("%-10s,"red");break;caseyellow:printf("%-10s","yellow");break;caseblue:printf("%-10s","blue");break;casewhite:printf("%-10s","white");break;caseblack:printf("%-10s","black");break;default:break;}}printf("\n");}}printf("\ntoal:%5d\n",n);}运行结果如下:1redyellowblue2redyellowwhite3redyellowblack4redblueyellow5redbluewhite6redblueblack7redwhiteyellow8redwhiteblue9redwhiteblack10redblackyellow。。。。。。。。。。。。。。。。。60blackwhitebluetota1:60有人说,不用枚举变量而用常数0表“红”,1表“黄………,不也可以吗?是的,完全可以。但显然用枚举变量更直观,因为枚举元素都选用了令人“见名知意”标识符,而且枚举变量的值限制在定义时规定的几个枚举元素范围内,如果赋予它一个其它的值,就会出现出错信息,便于检查。

10.10用typdef定义类型

除了可以直接使用C提供的标准类型名(如int.char.float.double.long等)和自己定义的结构体、共用体、指针、枚举类型外还可以用typedef定义新的类型名来代替已有的类型名如:typedefintINTEGER;typedeffloatREAL;指定用INTEGER代表int类型,REAL代表float.这样,以下两行等价:①inti,j;;floata,b;②INTEGERi,j;REALa,b;这样可以使熟悉F0RTRAN的人能用INTEGER和REALL定义变量,以适应他们的习惯。如果在一个程序中,一个整型变量用来计数,可以:TypedefintCOUNT;COUNTi,j;即将变量i.j定义为COUNT类型,而COUNT等价于int,因此i、j是整型。但在程序中将i,j定为COUNT类型,可以使人更一目了然地知道它们是用于计数的。可以定义结构体类型:typedefstruct{intmont;intday;intyear;}DATE;定义新类型名DATE,它代表上面定义的一个结构体类型。这时就可以用DATE定义变量:DATEbirthday;(不要写成structDATAbirthday;)DATA*p;(p为指向此结构体类型数据的指针)

还可以进一步:1typedefNUM[100];(定义NUM为整型数组类型)NUMn;(定义n为整型数组变量)2.typedefchar*STRING;(定义STRING为字符指针类型)STRINGp,s[10];(p为字符指变量,s为指针数组)3.typedint(*POINTER)()(定义POINTER为指向函数的指针类型,该函数返回整型值)POINTERp1,p2;(p1,p2为POINTER类型的指针变量)归纳起来,定义一个新的类型名的方法是:①先按定义变量的方法写出定义体(如:inti;)。②将变量名换成新类型名(如:将i换成COUNT)。③在最前面加typedef(如:typedefintCOUNT)。④然后可以用新类型名去定义变量。再以定义上述的数组类型为例来说明:①先按定义数组变量形式书写:intn[100];②将变量名n换成自己指定的类型名:intNUM[100]③在前面加上typedef,得到typedefintNUM[100];④用来定义变量:NUMn:同样,对字符指针类型,也是:①char*p;②char*STRING;③typedef*STRING;④STRINGp,s[10];习惯上常把用typedef定义的类型名用大写字母表示,以便与系统提供的标准类型标识符相区别。说明:1.用typedef可以定义各种类型名,但不能用来定义变量。用typedf可以定义出数组类型、字符串类型,使用比较方便。如定义数组,原来是用inta[10],b[10],c[10,d[10];由于都是一维数组,大小也相同,可以先将此数组类型定为一个名字:typedefintARR[10];然后用ARR去定义数组变量:ARRa,b,c,d;ARR为数组类型,它包含10个元素。因此,a,b,c,d都被定义为一维数组,含10个元素。可以看到,用typedef可以将数组类型和数组变量分离开来,利用数组类型可以定义多个数组变量。同样可以定义字符串类型、指针类型等。2.用typedef只是对已经存在的类型增加一个类型名,而没有创造新的类型。例如,前面定义的整型类型COUNT,它无非是对int型另给一个新名字。又如typedefintNUM[10];无非是把原来用"intn[10];"定义的数组变量的类型用一个新的名字NUM表示出来。无论用哪种方式定义变量,效果都是一样的。3.typedef与#define有相似之处,如typdefintCOUNT;和#defineCOUNTint的作用都是用COUNT代表int。但事实上,它们二者是不同的。#define是在预编译时处理的,它只能作简单的字符串替换,而typedef是在编译时处理的。实际上它并不是作简单的字符串替换,例如typedefintNUM[10]并不是用NUM[10]去代替int,而是采用如同定义变量的方法那样来定义一个类型(就是前面介绍过的将原来的变量名换成类型名).4.当不同源文件中用到同一类型数据(尤其是像数组、指针、结构体、共用体等类型数据)时,常用tyPedef定义一些数据类型,把它们单独放在一个文件中,然后在需要用到它们的文件中用#include命令把它们包含进来。5.使用tyPedef有利于程序的通用与移植。有时程序会依赖于硬件特性,用typedef便于移植。例如,有的计算机系统1nt型数据用两个字节,数值范围为一32768~32767,而另外一些机器则以4个字节存放一个整数,数值范围为+-21亿。如果把一个C程序从一个以4个字节存放整数的计算机系统移植到以2个字节存放整数的系统,按一般办法需要将定义变量中的每个int改为1ong。例如,将“inta,b,c;”,如果程序中有多处用int定义变量,则要改动多处。现可以用一个typedef定义:typedefintINTEGER;在程序中用INTEGER定义变量。在移植时只需改动typedef定义体即可:typedeflongINTEGER;oXlUiQfNcK8H5D2A+x*u$rZnWkShPeMaJ7G4C1z-w&t!pYmVjRgOdL9I6E3B0y(v%s#oXlTiQfNbK8H5D2A-x*u$qZnWkShPdMaJ7F4C1z)w&s!pYmUjRgOcL9H6E3B+y(v%r#oXlTiQeNbK8G5D2A-x*t$qZnVkShPdMaI7F4C0z)w&s!pXmUjRfOcL9H6E2B+y(u%r#oWlThQeNbJ8G5z)w&s!pYmUjRgOcL9I6E3B+y(v%r#oXlTiQeNbK8G5D2A-x*t$qZnVkShPdMaI7F4C0z)w&s!pXmUjRfOcL9H6E2B+y(u%r#oWlThQeNbJ8G5D1A-x*t$qYnVkSgPdMaI7F3C0z)v&s!pXmUiRfOcK9H6E2B+x(u%rZoWlThQeMbJ8G4D1A-w*t!qYnVjSgPdLaI6F3C0y)v&s#pXmUiRfNcK9H5E2B+x(u$rZoWkThQeMbJ7G4D1z-w*t!qYmVjSgOdLaI6F3B0y)v%s#pXlUiQfNcK8H5E2A+x*u$rZnWkThPeMaJ7G4C1z-w&t!qYmVjRgOdL9I6F3B0y(v%s#oXlUiQfNbK8H5D2A+x*u$qZnWkShPeMaJ7F4C1z)w&t!pYmUjRgOcL9I6E3B+y(v%r#oXlTiQfNbK8G5D2A-x*u$qZnVkShPdMaJ7F4C0z)w&s!pYmUjRfOcL9H6E3B+y(u%r#oWlTiQeNbJ8G5D1A-x*t$qYnVkSgPdMaI7F3C0z)v&s!pXmUjRfOcK9H6E2B+y(u%rZoWlThQeNbJ8G4D1A-w*t$qYnVjSgPdLaI7F3C0y)v&s#pXmUiRfNcK9H5E2B+x(u$rZoWkThQeMbJ8G4D1z-w*t!qYnVjSgOdLaI6F3C0y)v%s#pXlUiRfNcK8H5E2A+x(u$rZnWkThPeMbJ7G4C1z-w&t!qYmVjRgOdL9I6F3B0y)v%s#oXlUiQfNcK8H5D2A+x*u$rZnWkShPeMaJ7G4C1z)w&t!pYmVjRgOcL9I6E3B0y(v%r#oXlTiQfNbK8G5D2A-x*u$qZnVkShPdMaJ7F4C1z)w&s!pYmUjRgOcL9H6E3B+y(v%r#oWlTiQeNbK8G5D1A-x*t$qZnVkSgPdMaI7F4C0z)v&s!pXmUjRfOcK9H6E2B+y(u%r#oWlThQeNbJ8G5D1A-w*t$qYnVkSgPdLaI7F3C0z)v&s#TiQeNbK8G5D1A-x*t$qZnVkSgPdMaI7F4C0z)v&s!pXmUjRfOcL9H6E2B+y(u%r#oWlThQeNbJ8G5D1A-w*t$qYnVkSgPdLaI7F3C0z)v&s#pXmUiRfOcK9H5E2B+x(u%rZoWkThQeMbJ8G4D1A-w*t!qYnVjSgPdLaI6F3C0y)v&s#pXlUiRfNcK9H5E2A+x(u$rZoWkThPeMbJ7G4D1z-w&t!qYmVjSgOdL9I6F3B0y)v%s#pXlUiQfNcK8H5E2A+x*u$rZnWkThPeMaJ7G4C1z-w&t!pYmVjRgOdL9I6E3B0y(v%s#oXlTiQfNbK8H5D2A-x*u$qZnWkShPdMaJ7F4C1z)w&t!pYmUjRgOcL9I6E3B+y(v%r#oXlTiQeNbK8G5D2A-x*t$qZnVkShPdMaI7F4C0z)w&s!pXmUjRfOcL9H6E2B+y(u%r#oWlTiQeNbJ8G5D1A-x*t$qYnVkSgPdMaI7F3C0z)v&s!pXmUiRfOcK9H6E2B+x(u%rZoWlThQeMbJ8G4D1A-w*t!qYnVjSgPdLaI6F3C0y)v&s#pXmUiRfNcK9H5E2B+x(u$rZoWkThQeMbJ7G4D1z-w*t!qYmVjSgOdLaI6F3B0y)v%s#pXlUiQfNcK8H5E2A+x*u$rZnWkThPeMbJ7G4C1z-w&t!qYmVjRgOdL9I6F3B0y(v%s#oXlUiQfNbK8H5D2A+x*u$qZnWkShPeMaJ7F4C1z)w&t!pYmUjRgOcL9I6E3B0y(v%r#oXlTiQfNbK8G5D2A-x*u$qZnVkShPdMaJ7F4C0z)w&s!pYmUjRfOcL9H6E3B+y(u%r#oWlTiQeNbJ8G5D1A-x*t$qYnVkSgPdMaI7F4C0z)v&s!pXmUjRfOcK9H6E2B+y(u%rZoWlThQeN7F4C0z)w&s!pYmUjRfOcL9H6E3B+y(u%r#oWlTiQeNbK8G5D1A-x*t$qZnVkSgPdMaI7F4C0z)v&s!pXmUjRfOcK9H6E2B+y(u%rZoWlThQeNbJ8G4D1A-w*t$qYnVjSgPdLaI7F3C0y)v&s#pXmUiRfOcK9H5E2B+x(u%rZoWkThQeMbJ8G4D1z-w*t!qYnVjSgOdLaI6F3C0y)v%s#pXlUiRfNcK8H5E2A+x(u$rZnWkThPeMbJ7G4D1z-w&t!qYmVjSg

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论