第5章
自定义数据类型
学习目标了解结构体及结构体的各种操作。
掌握结构体成员和访问方法、理解结构体和函数之间的关系、掌握结构体变量作为 参数传递与返回结构体变量的函数方法。
掌握结构体中数组的用法、理解怎样创建结构体数组。
了解共用体的特点、定义和使用方法。
了解枚举类型定义和使用方法。
掌握类型定义。
结构体类型用struct定义,是用户自定义数据类型,它由不同类型的数据成员组成。
结构体变量在内存中占有一片连续的存储空间,结构体变量成员用点运算符和箭头运算符访问。
当数组元素为结构体类型时,称为结构体数组,其定义和访问遵循数组和结构体的语法规则。
共用体采用覆盖技术,使得共用体中的数据成员在内存中的存储是互相重叠的,每个数据成员都从相同的内存地址开始存储,当不会同时使用的数据项使用两种或者更多格式时,可以节省存储空间。
枚举类型从本质上说就是一个标签集合,采用枚举变量,是为了实现程序的可读性。
如对于颜色常说有赤、橙、黄、绿、青、蓝、紫7种,如果使用数字来表示状态,直接用0~6来表示就行,但直接用数字表示可读性差;而通过枚举类型声明eunmcolor{red,orange,yellow,green,cyan,blue,purple},就可以使程序的可读性提高。
结构和枚举类型从程序实现的角度来说,是用更接近自然语言的方式来表达数据。
如实现二维空间的点,虽然可以使用二维数组,但是可读性会较差。
若采用结构体,结构体中含有属性项x、y,这样就接近了实际使用的方式。
typedef是将已有的数据类型名命名为其他名称,这样有利于程序在不同的计算机系统中进行移植。
5.1结构体类型 C++提供了许多种基本的数据类型(如int、float、double、char等)供用户使用。
但 第5章自定义数据类型145 是由于程序需要处理的问题往往比较复杂,而且呈多样化,已有的数据类型显得不能满足使用要求。
在程序设计中,处理的数据对象由几个数据组成,而这些数据之间有着密切的联系,它们用来描述一个事物的几个方面,但它们并不属于同一数据类型。
例如,一个学生信息可以由姓名、性别、出生日期、联系方式等项目组成,在程序中,它们将表示为不同的数据类型,C++可以把这些不同类型的数据封装起来,作为整体处理。
C++允许用户根据需要自己声明一些类型,例如数组就是用户自己声明的数据类型。
此外,用户可以自己声明的类型还有结构体(structure)类型、共用体(union)类型、枚举(enumeration)类型、类(class)类型等,这些统称为用户自定义类型(User-DefinedType,UDT)。
由不同类型的数据元素组成的数据类型称为结构体。
5.1.1结构体类型的定义 在一个组合项中包含若干个类型不同(当然也可以相同)的数据项。
C++允许用户自己指定这样一种数据类型,它称为结构体。
它相当于其他高级语言中的记录(record)。
例如,可以通过下面的声明来建立如表5-1所示的数据类型。
structStudent{ intnum;charname[20];charsex;intage;floatscore;charaddr[30];}; //声明一个结构体类型Student(表名) //包括一个整型变量num(每个属性就是字段名)//包括一个字符数组name,可以容纳20个字符//包括一个字符变量sex//包括一个整型变量age//包括一个单精度型变量//包括一个字符数组addr,可以容纳30个字符//最后有一个分号 学号num… 姓名name[20] … 表5-1学生管理系统(Student) 性别 年龄 sex age … … 成绩score … 家庭住址addr[30] … 这样,程序设计者就声明了一个结构体类型Student(struct是声明结构体类型时所必须使用的关键字,不能省略),它向编译系统声明:这是一种结构体类型,它包括num、name、sex、age、score、addr等不同类型的数据项。
特别要注意的是,这里Student是一个类型名,它和系统提供的标准类型(如int、char、float、double)一样,都可以用来定义变量,只不过结构体类型需要事先由用户自己声明而已。
声明一个结构体类型的一般形式为 struct结构体类型名{ 成员表列 }; 146C++程序设计简明综合教程 结构体类型名用来作为结构体类型的标志。
上面的声明中Student就是结构体类型名。
大括号内是该结构体中的全部成员(member),由它们组成一个特定的结构体。
上例中的num、name、sex、score等都是结构体中的成员。
在声明一个结构体类型时必须对各成员都进行类型声明即:类型名成员名;每一个成员也称为结构体中的一个域(field)。
成员表列又称为域表。
声明结构体类型的位置一般在文件的开头,所有函数(包括main函数)之前,以便本文件中所有的函数都能利用它来定义变量。
当然也可以在函数中声明结构体类型,若在函数中声明结构体,一般该结构体仅用于本函数。
C++结构体对于结构体的成员加以了扩充,结构体的成员既可以包括数据(即数据成员),又可以包括函数(即函数成员),以适应面向对象的程序设计。
但是由于C++提供了类(class)类型,一般情况下,不必使用带函数的结构体。
5.1.2结构体变量的定义 声明一种结构体类型,它只相当于一个模型,没有具体数据,系统不会分配实际的内存单元,为了能在程序中使用结构体类型的数据,应当定义结构体类型的变量,并在其中存放具体的数据。
定义结构体类型变量的方法有以下三种。
1.先声明结构体类型再定义变量名这种方法的定义具有如下一般形式: struct结构体名{ 成员表列};结构体名变量名表列; 如上面已定义了一个结构体类型Student,可以用它来定义结构体变量student1,student2。
Studentstudent1,student2; 以上定义了student1和student2为结构体类型Student的变量,即它们具有Student类型的结构,作为变量它们可以被赋给具体的值。
类型Student及相应变量示意如图5-1所示。
Student student1student2 学号num01051012010105101202 姓名name[20]ZhangShangLiuYing 性别sexMF 年龄age1918 成绩score88.685 家庭住址addr[30]ChangShaZhuZhou 图5-1结构体类型(Student)与结构体变量(student1、student2) 图5-1在定义了结构体变量后,系统会为之分配内存单元。
例如,student1和student2在内存中各占63个字节(4+20+1+4+4+30=63)。
第5章自定义数据类型147
2.在声明类型的同时定义变量这种方法的定义具有如下一般形式: struct结构体名{ 成员表列}变量名表列; 例如: structStudent{ intnum;charname[20];charsex;intage;floatscore;charaddr[30];}student1,student2; //声明结构体类型Student//定义两个结构体类型Student的变量student1,student2
3.直接定义结构体类型变量直接定义结构体类型变量的一般形式为: struct{成员表列}变量名表列; //注意没有结构体类型名 例如: struct{ intnum;charname[20];charsex;intage;floatscore;charaddr[30];}student1,student2; //声明结构体类型,但没有类型名//定义两个结构体类型Student的变量student1,student2 这种方法虽然合法,但很少使用。
一般提倡先定义类型后定义变量的第一种方法。
但在程序比较简单,结构体类型只在本文件中使用的情况下,也可以用第二种方法。
关于结构体类型,有以下几点要说明。
(1)不要误认为凡是结构体类型都有相同的结构。
实际上,每一种结构体类型都有自己的结构,可以定义出许多种具体的结构体类型。
(2)类型与变量是不同的概念,不要混淆。
只能对结构体变量中的成员赋值,而不能 148C++程序设计简明综合教程 对结构体类型赋值。
在编译时,是不会为类型分配空间的,只为变量分配空间。
(3)对结构体中的成员(即“域”),可以单独使用,它的作用与地位相当于普通变量。
(4)结构体的成员也可以是一个结构体变量。
如: structDate{ intmonth;intday;intyear;};structStudent{intnum;charname[20];charsex;intage;Datebirthday;charaddr[30];}student1,student2; //声明一个结构体类型Date//声明一个结构体类型Student//定义student1和student2为结构体类型Student的变量 Student的结构如图5-2所示。
Studentnumname[20]sexage birthday addr[30] monthdayyear 图5-2含有结构体类型成员birthday的Student结构体
(5)结构体中的成员名可以与程序中的变量名相同,但二者没有关系。
例如,程序中可以另定义一个整型变量num,它与student中的num是两回事,互不影响。
5.1.3结构体变量的引用和初始化
1.结构体变量的引用定义了结构体变量之后,可以引用这个变量。
对于非指针型结构体变量,是通过成员运算符“.”,逐个访问其成员,一般格式为: 结构体变量名.成员名 例如,可以这样对变量的成员赋值: student1.num=0105101201; 对于指针型结构体变量,是通过“->”运算符来访问其成员,当然也可以用“.”运算
符来访问,一般格式为: 指针->成员名或 *(指针).成员名 例如,定义Student*student3=&student1;之后,可以这样对变量的成员赋值: 第5章自定义数据类型149 (student3).num=0105101201;或student3->num=0105101201; 注意:在利用指针引用结构体成员时,-和>之间不能有空格。
如果结构体成员本身又是一个结构体类型,则只能通过多级的分量运算,对最低一级的成员进行引用,此时,引用的格式扩展为: 结构体变量名.成员名.子成员名.….最低级子成员名 例如,对变量student1的成员day赋值: student1.birthday.day=23; “.”和“->”是结构体成员引用运算符,是优先级最高的运算符,具有左结合性。
对于结构体变量的引用,有以下几点说明。
(1)可以将一个已赋值的结构体变量的值赋给另一个具有相同结构的结构体变量。
方法是: 结构体变量名1=结构体变量名2; 如上面的student1和student2都是Student类型的变量,当student1已有值时,可以这样赋值: student2=student1;
(2)对结构体成员变量的使用可像普通变量那样进行,如进行赋值,参加运算等。
如: Studentstudent1,student2;student1.num=9901;student2.num=s1.num+1;student1.age++; 由于“.”运算符的优先级最高,student1.age++相当于(student1.age)++。
++是对student1.age进行自加运算,而不是先对age进行自加运算。
(3)不能整体引用一个结构体变量,将结构体变量作为一个整体进行输入和输出。
例如,已定义student1和student2为结构体变量,并且它们已有值。
不能企图这样输 出结构体变量中的各成员的值: cin>>student1; 只能对结构体变量中的各个成员分别进行输入和输出。
(4)可以引用结构体变量成员的地址,也可以引用结构体变量的地址。
如: cout<<&student1;cout<<&student1.age; //输出student1的首地址//输出student1.age的地址 结构体变量的地址主要用作函数参数,将结构体变量的地址传递给形参。
2.结构体变量的初始化结构体变量的初始化就是在定义结构体变量的同时,为其成员提供初值,可采用下述方法。
(1)在定义结构体类型的同时,为结构体变量初始化。
150C++程序设计简明综合教程 struct结构体名{ 成员列表}变量名={初始值列表}; 例如: structStudent{ intnum;charname[20];charsex;intage;Datebirthday;charaddr[30];}student1={0105101201,"ZhangShang",'M',19,5,17,1993,"ChangSha"}; 上述变量student1中的数据及其所占内存空间情况如图5-3所示。
student1.numstudent1.namestudent1.sexstudent1.agestudent1.birthday.monthstudent1.birthday.daystudent1.birthday.yearstudent1.addr 0105101201ZhangShangM195171993ChangSha 4字节20字节1字节4字节4字节4字节4字节30字节 12字节 图5-3变量student1数据分配图 注意:赋初值时,{}中间的数据顺序必须与结构体成员的定义顺序一致,否则就会出现混乱。
(2)利用已有的结构体类型定义结构体变量,并同时初始化。
即采取声明类型与定义变量分开的形式,在定义变量时进行初始化。
格式是: 结构体名称变量名={值
1,值2,…}; 如: Studentstudent1={0105101201,"ZhangShang",'M',19,5,17,1993,"ChangSha"}; 此时,student1变量中的num成员的值为0105101201,name成员的值为"ZhangShang",sex成员的值为'M',等等。
(3)结构体变量在程序中赋值。
如果定义结构体变量时未对其赋初值,那么在程序中只能一个一个地对其成员逐一赋 值,或者用已赋值的同类型的结构体变量对其赋值。
【例5-1】引用结构体变量中的成员。
第5章自定义数据类型151 #include
usingnamespacestd;
structDate
//声明结构体类型Date
{
intmonth;
intday;
intyear;
};
structStudent
//声明结构体类型Student
{
intnum;
charname[20];
charsex;
Datebirthday;
//声明birthday为Date类型的成员
floatscore;
}student1,student2={0105101202,"LiuYing",'F',18,6,22,1994,85};
//定义Student类型的变量student1,student2,并对student2初始化
intmain()
{
student1=student2;
//将student2各成员的值赋予student1的相应成员
cout<student1中的birthday各成员的值
cout<
0105101202LiuYingF186/22/199485
【例5-2】计算学生7门功课的平均成绩、最高分和最低分。
#includeusingnamespacestd;structScore{doublegrade[7];doubleaver,max,min;}intmain(){inti;Scorem;
152C++程序设计简明综合教程
for(i=0;i<7;i++)cin>>m.grade[i];
m.aver=m.grade[0];m.max=m.grade[0];m.min=m.grade[0];for(i=1;i<7;i++){
m.aver=m.aver+m.grade[i];if(m.maxm.grade[i])m.min=m.grade[i];
}m.aver=m.aver/7;cout<如果有10个学生的数据需要参加运算,显然应该用数组,这就是结构体数组。
结构体数组与数值型数组的不同之处在于:每个数组元素都是一个结构体类型的数据,它们都分别包括各个成员项。
1.结构体数组的定义定义结构体数组和定义结构体变量的方法相仿,定义结构体数组时只需声明其为数组即可。
如: structStudent{ intnum;charname[20];charsex;intage;floatscore;charaddr[30];};Studentstu[3]; //声明结构体类型Student//定义Student类型的数组stu 可以直接定义一个结构体数组,如: structStudent{ intnum;charname[20]; 第5章自定义数据类型153 charsex;intage;floatscore;charaddr[30];}stu[3]; 或 struct{ intnum;charname[20];charsex;intage;floatscore;charaddr[30];}stu[3];
2.结构体数组初始化结构体数组的初始化与其他类型的数组一样,对结构体数组可以初始化。
如: structStudent{ intnum;charname[20];charsex;intage;floatscore;charaddr[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[]={{…},{…},{…}}; 编译时,系统会根据给出初值的结构体常量的个数来确定数组元素的个数。
数组的初始化也可以用以下形式。
Studentstu[]={{…},{…},{…}};//已事先声明了结构体类型Student 由上可以看到,结构体数组初始化的一般形式是: 结构体名数组名[]={初值表列}; 注意:一个结构体常量应包括结构体中全部成员的值。
3.结构体数组的引用下面举一个简单的例子来说明结构体数组的定义和引用。
154C++程序设计简明综合教程 【例5-3】统计候选人得票数目的程序。
设有三个候选人,最终只能有一人当选为领导。
今有10个人参加投票,从键盘先后输入这10个人所投的候选人的名字,要求最后输出这三个候选人的得票结果。
可以定义一个候选人结构体数组,包括三个元素,在每个元素中存放一个候选人的数据。
程序如下。
#include
usingnamespacestd;
structPerson
//声明结构体类型Person
{
charname[20];
intcount;
};
intmain()
{
Personleader[3]={"Li",
0,"Zhang",
0,"Fun",0}; //定义Person类型的数组,内容为三个候选人的姓名和当前的得票数 inti,j; charleader_name[20]; //leader_name为投票人所选的人的姓名 for(i=0;i<10;i++) { cin>>leader_name; //先后输入10张票上所写的姓名 for(j=0;j<3;j++) //将票上姓名与三个候选人的姓名比较 if(strcmp(leader_name,leader[j].name)==0) leader[j].count++;
//如果与某一候选人的姓名相同,就给他加一票 } cout<0;
}
程序定义一个的结构体数组leader;它有三个元素,每一元素包含两个成员,即name(姓名)和count(得票数)。
在定义数组时使之初始化,使三位候选人的票数都先 置零。
在这个例子中,也可以不用字符数组而用string方法的字符串变量来存放姓名数据, 程序可修改如下。
#include#includeusingnamespacestd;structPerson{
stringname;
//成员name为字符串变量
第5章自定义数据类型155
intcount;};
intmain()
{
Personleader[3]={"Li",
0,"Zhang",
0,"Fun",0}; inti,j; stringleader_name; //leader_name为字符串变量 for(i=0;i<10;i++) { cin>>leader_name; for(j=0;j<3;j++) if(leader_name==leader[j].name)
//直接用"=="进行比较 leader[j].count++; } cout<0;
}
5.1.5结构体指针
一个结构体变量的指针就是该变量所占据的内存段的起始地址。
可以设一个指针变量,用来指向一个结构体变量,此时该指针变量的值是结构体变量的起始地址。
指针变量也可以用来指向结构体数组中的元素。
1.指向结构体变量的指针结构体变量的指针:是指结构体变量所占内存单元的起始地址。
因此,可以定义指针变量指向结构体变量。
此时该指针变量的值就是结构体变量在内存中的起始地址。
【例5-4】使用指向结构体变量的指针。
#includeusingnamespacestd;structstudent{longintnum;charname[20];charsex;floatscore;};voidmain(){
//定义结构体类型student
156C++程序设计简明综合教程
studentstu1;
//定义结构体类型student的变量stu1
student*p;
//定义student类型的指针变量p
p=&stu1;
//将结构体变量stu1的地址赋给指针变量p
stu1.num=1;
//分别给结构体变量stu1的各成员赋值
strcpy(stu1.name,"lilin");
stu1.sex='M';
stu1.score=89;
//输出stu1各成员的值
cout<num<<"\t"<name<<"\t"<sex<<"\t"<score<
1 lilinM 89
1 lilinM 89
1 lilinM 89 可见,三种访问结构体变量各成员的值的结果完全相同。
说明:
(1)例5-4中(*p).num也可写为p->num。
(2)结构体取成员的运算可以采用以下三种形式。
①结构体变量名.成员名②(*结构体指针变量名).成员名③结构体指针变量名->成员名
2.指向结构体数组元素的指针如果结构体变量的指针初始化为指向结构体数组的首元素,则该指针变量的值就是结构体数组在内存中的起始地址;然后通过指针变量的自增运算可以依次指向数组每个元素。
【例5-5】使用指向结构体数组元素的指针。
#includeusingnamespacestd;structstudent{intnum;
charname[20];charsex;intage;};studentstu[3]={{
1,"lilin",'M',18},{
2,"sum",'M',19},{
3,"zhao",'M',20}};voidmain() 第5章自定义数据类型157 {student*p; for(p=stu;pnum<<"\t"<name<<"\t"<sex
}
<<"\t"<age<1
lilinM
18
2 sum
M 19
3 zhaoM 20 说明:把stu赋给指针变量p,就表示p指向了该数组的首元素(p的值为数组的起始地址)。
p++表示p指向数组的下一个元素,利用这种方法可以访问数组中所有元素的值。
5.1.6结构体与函数 与int、char、float、double等类型一样,结构体变量可作为函数的参数和函数的返回值。
1.结构体类型作为函数参数结构体类型作为函数的参数有三种形式:传值、传址和引用。
1)用结构体类型的变量作函数的参数(传值)在C++语言中,允许把结构体类型的变量直接作为函数的参数,但要注意:此时主调函数的调用点上的实参与被调函数相应位置上的形参必须是相同的结构体类型,是将实参的值(各成员的值)传递给相应的形参。
【例5-6】用结构体类型的变量作为函数的参数。
#includeusingnamespacestd;structstudent{
intnum;charname[20];charsex;intage;};voidprint(students);studentstu[3]={{
1,"lilin",'M',18},{
2,"sum",'M',19},'M',20}};voidmain(){inti;for(i=0;i<3;i++) print(stu[i]); {
3,"zhao", 158C++程序设计简明综合教程 }voidprint(students){ cout<1
lilinM
18
2 sum
M 19
3 zhaoM 20 可见,当把一个结构体类型的变量作为函数的参数时,可以将该变量的值(包含结构体类型中各成员的值)传递给被调函数的形参。
2)用指向结构体的指针作函数的参数(传址)用指向结构体变量的指针作参数,这种方式同指针作为函数的参数的原理一致,传递的是结构体变量的地址(指针)。
【例5-7】用指针的方法实现例5-6程序的功能。
#include"iostream"usingnamespacestd;structstudent{intnum;charname[20];charsex;intage;};voidprint(student*);studentstu[3]={{
1,"lilin",'M',18},{
2,"sum",'M',19},{
3,"zhao",'M',20}};voidmain(void){ inti;for(i=0;i<3;i++) print(&stu[i]);}voidprint(student*s){cout<num<<"\t"<name<<"\t"<sex<<"\t"<age<<"\t"<
【例
5-8】用结构体变量引用的方法实现例5-7程序的功能。
#include
第5章自定义数据类型159
usingnamespacestd;structstudent{intnum;charname[20];
charsex;intage;};voidprint(student&);studentstu[3]={{
1,"lilin",'M',18},{
2,"sum",'M',19},{
3,"zhao",'M',20}};voidmain(){inti;for(i=0;i<3;i++)print(stu[i]);}voidprint(student&s){ cout<2.返回结构体类型的函数若函数的返回值是结构体类型,则为返回结构体类型的函数。
返回结构类型值的函数 的定义格式如下。
结构体名称{ 函数体} 函数名(形参及类型说明) 【例5-9】定义一个返回结构体类型的函数,求所有同学中年龄最大的同学。
#includeusingnamespacestd;structstudent{intnum;charname[20];charsex;intage;};studentmax(student*,int);studentstu[3]={{
1,"lilin",'M',18},{
2,"sum",'M',19},{
3,"zhao",'M',20}};voidmain(){ studentmaxold=max(stu,3);cout<age;index=0;for(i=0;iage){
index=i;age1=(s+i)->age;}return(*(s+index));}
3.返回结构体引用的函数返回结构体引用的函数与返回其他类型引用的函数的定义方法和原理一致,但注意不要返回一个局部变量的引用即可。
5.2共用体类型 共用体提供了一种可以把几种不同类型的数据存放于同一段内存的机制。
这种使几个不同的变量占用同一段内存空间的结构称为共用体。
共用体成员互相覆盖,不能同时引用。
5.2.1共用体类型与变量的定义 有时需要使几种不同类型的变量存放到同一段内存单元中。
例如,可把一个整型变量、一个字符型变量、一个单精度型变量放在同一个地址开始的内存单元中(如图5-4所示)。
地址1000100110021003 字符变量整型变量 单精度变量 图5-4将几种不同类型的变量存放到同一段内存单元中 从图5-4看出,以上三个变量在内存中占的字节数不同,但都从同一地址开始存放。
也就是使用了覆盖技术,几个变量互相覆盖。
这种使几个不同的变量共占同一段内存的结构,称为共用体(union)类型的结构(有些书中译为联合)。
声明共用体类型的一般形式为: union共用体类型名{ 第5章自定义数据类型161 成员表列}; 如: unionData{ charch;inti;floatf;}; 共用体Data包含三个成员,它们使用同一地址的内存,共用体的大小是成员中占内存最大的成员的大小。
某个时刻只能存放某一成员,不同时刻存放的成员可能不同。
与结构体变量定义类似,可以声明共用体之后,再定义共用体变量;也可在声明共用体类型的同时定义共用体变量;也可没有共用体类型名而直接定义共用体变量。
声明共用体类型后,定义共用体变量的一般形式为: 共用体类型名共用体变量名; “共用体”与“结构体”的定义形式相似。
但它们的含义是不同的。
结构体变量所占内存长度是各成员占的内存长度之和。
每个成员分别占有其自己的内存单元。
共用体变量所占的内存长度等于最长的成员的长度。
5.2.2共用体变量的引用 对共用体成员的引用与对结构体成员的引用相同,如果通过共用体变量来引用成员,使用“.”,如果通过共用体指针来引用成员,则要使用“->”或“.”。
如:Dataa,*p;例如,下面的引用方式是正确的。
a.i,*p->i,(*p).i(引用共用体变量中的整型成员i)a.ch,*p->ch,(*p).ch(引用共用体变量中的字符型成员ch)a.f,*p->f,(*p).f(引用共用体变量中的双精度型成员d)不能只引用共用体变量,例如: cout<几点说明:
(1)使用共用体变量的目的是希望用同一个内存段存放几种不同类型的数据。
但请注意:在每一瞬时只能存放其中一种,而不是同时存放几种。
(2)能够访问的是共用体变量中最后一次被赋值的成员,在对一个新的成员赋值后原 162C++程序设计简明综合教程 有的成员就失去作用。
(3)共用体变量的地址和它的各成员的地址都是同一地址。
(4)不能对共用体变量名赋值;不能企图引用变量名来得到一个值;不能在定义共用 体变量时对它初始化;不能用共用体变量名作为函数参数。
5.2.3共用体与结构体的联合使用 共用体类型可以出现在结构体类型声明中,也可定义共用体数组。
反之,结构体也可出现在共用体类型定义中,数组也可成为共用体成员。
【例5-10】设有若干个人员的数据,其中有学生和教师。
学生的数据中包括:姓名、号码、性别、职业、年级。
教师的数据中包括:姓名、号码、性别、职业、职务。
可以看出,学生和教师所包含的数据是不同的。
现要求把它们放在同一表格中,如果job项为s(学生),则第5项为grade(年级)。
Li是3年级的。
如果job项是t(教师),则第5项为position(职务)。
Wang是prof(教授)。
显然对第5项可以用共用体来处理(将class和position放在同一段内存中)。
要求输入人员的数据,然后再输出。
为简化起见,只设两个人(一个学生、一个教师)。
程序如下。
#include
#include
#include
//在输出流中使用了控制符setw
usingnamespacestd;
struct
//声明结构体
{
intnum;
charname[10];
charsex;
charjob;
unionP
//声明共用体类型(包含在结构体内)
{
intgrade;
//年级
charposition[10];
//职务
}category;
//成员category为共用体类型P的变量
}person[2];
//定义结构体数组person,含两个元素
intmain()
{
inti;
for(i=0;i<2;i++)
//输入两个学生的数据
{
cin>>person[i].num>>person[i].name>>person[i].sex>>person[i].job;
if(person[i].job=='s')
cin>>person[i].category.grade;
//若是学生则输入年级
else
第
5章自定义数据类型163 if(person[i].job=='t')cin>>person[i].category.position;//若是教师则输入职务 }cout<(6)<(6)<
101Lifs3↙(注意在输入的字母f和s之间无空格)102Wangmtprof↙(注意在输入的字母m和t之间无空格)No.Namesexjobgrade/position101Lifs3102Wangmtprof
5.3枚举类型
枚举是一个被命名的整型常数的集合。
如果一个变量只有几种可能的值,可以定义为枚举(enumeration)类型。
而“枚举”就是将变量的值一一列举出来,变量的取值只能在列举出来的值的范围内。
声明枚举类型用enum开头。
例如: enumweekday{sun,mon,tue,wed,thu,fri,sat}; 上面声明了一个枚举类型weekday,花括号中sun,mon,…,sat等称为枚举元素或枚举常量。
表示这个类型的变量值只能是以上7个值之
一。
枚举元素是用户自己定义的标识符。
声明枚举类型的一般形式为: enum枚举类型名{ 枚举常量标识符[=整型常数],枚举常量标识符[=整型常数],…枚举常量标识符[=整型常数],}; 在声明了枚举类型之后,可以用它来定义变量。
其一般形式为: [enum]枚举类型名枚举变量列表; 164C++程序设计简明综合教程 在C++中枚举类型名可包含关键字enum,也允许不写enum,一般也不写enum。
如: weekdayworkday,week_end; 这样,workday和week_end被定义为枚举类型weekday的变量。
根据以上对枚举类型weekday的声明,枚举变量的值只能是sun到sat之
一。
例如: workday=tue;week_end=sat;//这两句都是正确的 与结构体变量和共用体变量定义类似,也可以直接定义枚举变量,如: enum{sun,mon,tue,wed,thu,fri,sat}workday,week_end; 注意:这些标识符(枚举元素)并不自动地代表什么含义。
对枚举类型的几点说明:
(1)在C++中,枚举元素是作常量处理的,所以枚举元素不是变量,不能对它们赋值,如:sun=0;mon=1;不对。
(2)枚举元素作为常量,它们是有值的,如果枚举常量标识符省略“=整型常数”,则C++按定义时的顺序对它们赋值为0、1、2、
3、…,但当枚举常量标识符中某个成员赋值后,其后的成员则按此数值依次加1作为其序列号。
若有定义:enumWeekday{sun,mon,tue,wen,thu,fri,sat}workday;,则枚举元素sun的值为0,mon元素的值为
1,…,sat元素的值为
6。
若有定义:enumWeekday{mon=1,tue,wen,thu,fri,sat,sun}workday;,则枚举元素mon元素的值为
1,…,sun元素的值为
7。
如果有赋值语句:workday=thu;,则说明变量workday的值为
4。
(3)枚举值可以用来做判断比较。
如: if(workday(4)一个整数不能直接赋给一个枚举变量。
如:workday=4;,这是错误的,因为workday与4不是同一数据类型,应进行类型的强制转换之后才能赋值。
如: workday=(Weekday)4; 上述语句是将序号为4的枚举元素赋给worday,等价于: workday=thu; 【例5-11】口袋中有红、黄、蓝、白、黑5种颜色的球若干个。
每次从口袋中任意取出三个球,问得到三种不同颜色的球的可能取法,输出每种排列的情况。
#include
#include
//在输出时要用到setw控制符
usingnamespacestd;
intmain()
{
enumcolor{red,yellow,blue,white,black};//声明枚举类型color
colorpri;
//定义color类型的变量pri
inti,j,k,n=0,loop;
//n是累计不同颜色的组合数
第5章自定义数据类型165
for(i=red;i<=black;i++)
//当i为某一颜色时
for(j=red;j<=black;j++)
//当j为某一颜色时
if(i!
=j) //若前两个球的颜色不同 { for(k=red;k<=black;k++) //只有前两个球的颜色不同,才需要检查第三个球的颜色 if((k!
=i)&&(k!
=j)) //三个球的颜色都不同 { n=n+1; //使累计值n加
1 cout<(3)<3
for(loop=1;loop<=3;loop++)//先后对三个球作处理
{
switch(loop)
//loop的值先后为1,2,
3 { case1:pri=color(i);break; //color(i)是强制类型转换,使pri的值为i case2:pri=color(j);break;//使pri的值为j case3:pri=color(k);break;//使pri的值为k default:break; }switch(pri) //判断pri的值,输出相应的"颜色" { casered:cout<(8)<<"red";break;
caseyellow:cout<(8)<<"yellow";break;
caseblue:cout<(8)<<"blue";break;
casewhite:cout<(8)<<"white";break;
caseblack:cout<(8)<<"black";break;
default:break;
}
}
cout<但显然用枚举变量更直观,因为枚举元素都选用了令人“见名知意”的标识符,而且枚举变量的值被限制在定义时规定的几个枚举元素范围,如果赋予它一个其他的值,就会出现出错信息,便于检查。
5.4类型定义 在C++语言中,除了可以直接用C++提供的标准类型名int、char、floot、double、long 166C++程序设计简明综合教程 等直接声明结构体、共用体、枚举等类型外,还可以用typedef定义新的类型名代替已有的类型名。
用typedef定义新的类型名的格式为: typedef类型名标识符 如: typedefintINTEGER;typedeffloatREAL; //指定用标识符INTEGER代表int类型//指定用REAL代表float类型 这样,以下两行等价: inti,j;floata,b;INTEGERi,j;REALa,b; 这样可以使熟悉其他高级语言的人能用INTEGER和REAL定义变量,以适应他们在其他高级语言学习中形成的习惯。
如果在一个程序中,整型变量是专门用来计数的,可以用COUNT来作为整型类型名: typedefintCOUNT; //指定用COUNT代表int型 COUNTi,j; //将变量i,j定义为COUNT类型,即int类型 在程序中将变量i、j定义为COUNT类型,可以使人们更一目了然地知道它们是用于 计数的。
也可以声明构造类型,其格式为: typedef构造类型{ 成员表列;}标识符; //如struct,若在构造类型之前用了关键字typedef,表示是声明新名//注意标识符是新类型名,而不是变量名 如: typedefstruct{intyear,month,day;}DATE; 所声明的新类型名DATE代表上面指定的一个结构体类型。
这样就可以用DATE定义变量: DATEbirthday;DATE*p; //p为指向此结构体类型数据的指针 还可以进一步列出typedef的一些使用形式。
(1)用typedef为某一数组类型指定新关键字。
typedefcharSTRING[81];//声明STRING为字符数组类型,包含81个元素 STRINGstr1; //等价于charstr1[81]; typedefintNUM[100];//声明NUM为整型数组类型,包含100个元素 第5章自定义数据类型167 NUMn; //定义n为包含100个整型元素的数组,等价于intn[100];
(2)用typedef为某一指向函数的指针类型指定新关键字。
typedef(int*)(*P_Fun)(int,int);//P_Fun为指向函数的指针类型(函数的返回值为int*,带两个int参数) P_Funpfun[5];//pfun为长度为5的函数指针数组 等价于: int*(*pfun[5])(int,int); 归纳起来,声明一个新的类型名的方法如下。
(1)先按定义变量的方法写出定义语句(如inti;)。
(2)将变量名换成新类型名(如将i换成COUNT)。
(3)在最前面加typedef(如typedefintCOUNT)。
(4)然后可以用新类型名去定义变量。
再以声明上述的数组类型为例来说明。
(1)先按定义数组形式书写:intn[100];。
(2)然后将变量名n换成自己指定的类型名: intNUM[100];
(3)再在前面加上typedef,得到 typedefintNUM[100];
(4)最后就可以用新类型名来定义变量:NUMn;(n是包含100个整型元素的数组)。
习惯上常把用typedef声明的类型名用大写字母表示,以便与系统提供的标准类型标 识符相区别。
关于typedef有以下几点说明: (1)typedef可以声明各种类型名,但不能用来定义变量。
用typedef可以声明数组类型、字符串类型,使用比较方便。
(2)用typedef只是对已经存在的类型增加一个类型名,而没有创造新的类型。
(3)当在不同源文件中用到同一类型数据(尤其是像数组、指针、结构体、共用体等类型数据)时,常用typedef声明一些数据类型,把它们单独放在一个头文件中,然后在需要用到它们的文件中用#include命令把它们包含进来,以提高编程效率。
(4)使用typedef有利于程序的通用与移植。
有时程序会依赖于硬件特性,用typedef便于移植。
习题
5 一、选择题
1.有以下程序段 168C++程序设计简明综合教程 structst{intx;int*y; }*pt;inta[]={1,2},b[]={3,4};stc[2]={10,a,20,b};pt=c; 以下选项中表达式的值为11的是(
A.*pt->y B.pt->x
2.有以下说明和定义语句 )。
C.++pt->x
D.(pt++)->x structstudent{intage;charnum[8];};studentstu[3]={{20,"200401"},{21,"200402"},{19,"200403"}};student*p=stu; 以下选项中引用结构体变量成员的表达式错误的是()。
A.(p++)->num B.p->num
C.(*p).num
3.有以下程序 D.stu[3].age #include
unionpw
{
inti;
charch[2];
}a;
voidmain()
{
a.ch[0]=13;
a.ch[1]=0;
cout<(注意:ch[0]在低字节,ch[1]在高字节。
) A.13 B.14 C.208 D.209
4.设有如下枚举类型定义 enumlanguage{Basic=
3,Assembly=
6,Ada=100,COBOL,Fortran}; 枚举量Fortran的值为()。
A.
4 B.
7 5.以下叙述中错误的是()。
C.102 D.103 第5章自定义数据类型169
A.可以通过typedef增加新的类型
B.可以用typedef将已存在的类型用一个新的名字来代表
C.用typedef定义新的类型名后,原有类型名仍有效
D.用typedef可以为各种类型起别名,但不能为变量起别名
二、填空题
1.若有以下定义和语句,则sizeof(a)的值是 ,而sizeof(b)的值是 。
可用a.day引用结构体成员day,请写出引用结构体成员a.day的其他两种形 式 、 。
struct{ intday;charmouth;intyear;}a,*b;b=&a;
2.设有以下结构类型说明和变量定义,则变量a在内存所占字节数是 。
structstud { charnum[6]; int s[4]; doubleave; }a,*p;
3.若有以下说明和定义语句,则变量w在内存中所占的字节数是 。
unionaa{floatx,y;charc[6];};structst{unionaav;floatw[5];doubleave;}w;
4.下面程序的输出是 。
#includeusingnamespacestd;voidmain(){
enumteam{my,your=4,his,her=his+10};
170C++程序设计简明综合教程
cout<5.以下程序的运行结果是
。
#include#includeusingnamespacestd;typedefstructstudent{
charname[10];longsno;floatscore;}ST;main(){STa={"zhangsan",2001,95},b={"Shangxian",2002,90},c={"Anhua",2003,95},d,*p=&d;d=a;if(strcmp(a.name,b.name)>0)d=b;if(strcmp(c.name,d.name)>0)d=c;cout<name<6.填空完成以下程序,实现在结构体数组中查找并输出分数最高和最低的同学姓名
和成绩。
#include
usingnamespacestd;
structStudent
{
charname[8];
intscore;
};
voidmain()
{
intmax,min,i,j,num;
Studentstud[]={"李平",92,"王兵",72,"钟虎",83,"孙逊",60,"徐军",88};
(1) ;
(2) ; for(i=1;i(3)
)
max=i;
else
if(stud[i].score(4)
;
第
5章自定义数据类型171 }
(5) ; }
三、编程题
1.编程:Input和Output函数输入/输出5个学生的数据记录,其中每个学生包含学号、姓名、5门课程分数的信息。
2.使用结构类型表示复数,编程输入两个复数,可以选择进行复数的+、-、×和÷运算,并输出结果。
3.将一个班的学生姓名和成绩输入到结构体数组中,寻找并输出最高分的学生。
4.使用X-Y平面直角坐标系上的点,编写程序读入一个四边形,判别由这4个点的连线构成的图形是否为正方形、矩形或其他四边形。
(要求:定义求两点距离的函数使用结构参数)。
掌握结构体成员和访问方法、理解结构体和函数之间的关系、掌握结构体变量作为 参数传递与返回结构体变量的函数方法。
掌握结构体中数组的用法、理解怎样创建结构体数组。
了解共用体的特点、定义和使用方法。
了解枚举类型定义和使用方法。
掌握类型定义。
结构体类型用struct定义,是用户自定义数据类型,它由不同类型的数据成员组成。
结构体变量在内存中占有一片连续的存储空间,结构体变量成员用点运算符和箭头运算符访问。
当数组元素为结构体类型时,称为结构体数组,其定义和访问遵循数组和结构体的语法规则。
共用体采用覆盖技术,使得共用体中的数据成员在内存中的存储是互相重叠的,每个数据成员都从相同的内存地址开始存储,当不会同时使用的数据项使用两种或者更多格式时,可以节省存储空间。
枚举类型从本质上说就是一个标签集合,采用枚举变量,是为了实现程序的可读性。
如对于颜色常说有赤、橙、黄、绿、青、蓝、紫7种,如果使用数字来表示状态,直接用0~6来表示就行,但直接用数字表示可读性差;而通过枚举类型声明eunmcolor{red,orange,yellow,green,cyan,blue,purple},就可以使程序的可读性提高。
结构和枚举类型从程序实现的角度来说,是用更接近自然语言的方式来表达数据。
如实现二维空间的点,虽然可以使用二维数组,但是可读性会较差。
若采用结构体,结构体中含有属性项x、y,这样就接近了实际使用的方式。
typedef是将已有的数据类型名命名为其他名称,这样有利于程序在不同的计算机系统中进行移植。
5.1结构体类型 C++提供了许多种基本的数据类型(如int、float、double、char等)供用户使用。
但 第5章自定义数据类型145 是由于程序需要处理的问题往往比较复杂,而且呈多样化,已有的数据类型显得不能满足使用要求。
在程序设计中,处理的数据对象由几个数据组成,而这些数据之间有着密切的联系,它们用来描述一个事物的几个方面,但它们并不属于同一数据类型。
例如,一个学生信息可以由姓名、性别、出生日期、联系方式等项目组成,在程序中,它们将表示为不同的数据类型,C++可以把这些不同类型的数据封装起来,作为整体处理。
C++允许用户根据需要自己声明一些类型,例如数组就是用户自己声明的数据类型。
此外,用户可以自己声明的类型还有结构体(structure)类型、共用体(union)类型、枚举(enumeration)类型、类(class)类型等,这些统称为用户自定义类型(User-DefinedType,UDT)。
由不同类型的数据元素组成的数据类型称为结构体。
5.1.1结构体类型的定义 在一个组合项中包含若干个类型不同(当然也可以相同)的数据项。
C++允许用户自己指定这样一种数据类型,它称为结构体。
它相当于其他高级语言中的记录(record)。
例如,可以通过下面的声明来建立如表5-1所示的数据类型。
structStudent{ intnum;charname[20];charsex;intage;floatscore;charaddr[30];}; //声明一个结构体类型Student(表名) //包括一个整型变量num(每个属性就是字段名)//包括一个字符数组name,可以容纳20个字符//包括一个字符变量sex//包括一个整型变量age//包括一个单精度型变量//包括一个字符数组addr,可以容纳30个字符//最后有一个分号 学号num… 姓名name[20] … 表5-1学生管理系统(Student) 性别 年龄 sex age … … 成绩score … 家庭住址addr[30] … 这样,程序设计者就声明了一个结构体类型Student(struct是声明结构体类型时所必须使用的关键字,不能省略),它向编译系统声明:这是一种结构体类型,它包括num、name、sex、age、score、addr等不同类型的数据项。
特别要注意的是,这里Student是一个类型名,它和系统提供的标准类型(如int、char、float、double)一样,都可以用来定义变量,只不过结构体类型需要事先由用户自己声明而已。
声明一个结构体类型的一般形式为 struct结构体类型名{ 成员表列 }; 146C++程序设计简明综合教程 结构体类型名用来作为结构体类型的标志。
上面的声明中Student就是结构体类型名。
大括号内是该结构体中的全部成员(member),由它们组成一个特定的结构体。
上例中的num、name、sex、score等都是结构体中的成员。
在声明一个结构体类型时必须对各成员都进行类型声明即:类型名成员名;每一个成员也称为结构体中的一个域(field)。
成员表列又称为域表。
声明结构体类型的位置一般在文件的开头,所有函数(包括main函数)之前,以便本文件中所有的函数都能利用它来定义变量。
当然也可以在函数中声明结构体类型,若在函数中声明结构体,一般该结构体仅用于本函数。
C++结构体对于结构体的成员加以了扩充,结构体的成员既可以包括数据(即数据成员),又可以包括函数(即函数成员),以适应面向对象的程序设计。
但是由于C++提供了类(class)类型,一般情况下,不必使用带函数的结构体。
5.1.2结构体变量的定义 声明一种结构体类型,它只相当于一个模型,没有具体数据,系统不会分配实际的内存单元,为了能在程序中使用结构体类型的数据,应当定义结构体类型的变量,并在其中存放具体的数据。
定义结构体类型变量的方法有以下三种。
1.先声明结构体类型再定义变量名这种方法的定义具有如下一般形式: struct结构体名{ 成员表列};结构体名变量名表列; 如上面已定义了一个结构体类型Student,可以用它来定义结构体变量student1,student2。
Studentstudent1,student2; 以上定义了student1和student2为结构体类型Student的变量,即它们具有Student类型的结构,作为变量它们可以被赋给具体的值。
类型Student及相应变量示意如图5-1所示。
Student student1student2 学号num01051012010105101202 姓名name[20]ZhangShangLiuYing 性别sexMF 年龄age1918 成绩score88.685 家庭住址addr[30]ChangShaZhuZhou 图5-1结构体类型(Student)与结构体变量(student1、student2) 图5-1在定义了结构体变量后,系统会为之分配内存单元。
例如,student1和student2在内存中各占63个字节(4+20+1+4+4+30=63)。
第5章自定义数据类型147
2.在声明类型的同时定义变量这种方法的定义具有如下一般形式: struct结构体名{ 成员表列}变量名表列; 例如: structStudent{ intnum;charname[20];charsex;intage;floatscore;charaddr[30];}student1,student2; //声明结构体类型Student//定义两个结构体类型Student的变量student1,student2
3.直接定义结构体类型变量直接定义结构体类型变量的一般形式为: struct{成员表列}变量名表列; //注意没有结构体类型名 例如: struct{ intnum;charname[20];charsex;intage;floatscore;charaddr[30];}student1,student2; //声明结构体类型,但没有类型名//定义两个结构体类型Student的变量student1,student2 这种方法虽然合法,但很少使用。
一般提倡先定义类型后定义变量的第一种方法。
但在程序比较简单,结构体类型只在本文件中使用的情况下,也可以用第二种方法。
关于结构体类型,有以下几点要说明。
(1)不要误认为凡是结构体类型都有相同的结构。
实际上,每一种结构体类型都有自己的结构,可以定义出许多种具体的结构体类型。
(2)类型与变量是不同的概念,不要混淆。
只能对结构体变量中的成员赋值,而不能 148C++程序设计简明综合教程 对结构体类型赋值。
在编译时,是不会为类型分配空间的,只为变量分配空间。
(3)对结构体中的成员(即“域”),可以单独使用,它的作用与地位相当于普通变量。
(4)结构体的成员也可以是一个结构体变量。
如: structDate{ intmonth;intday;intyear;};structStudent{intnum;charname[20];charsex;intage;Datebirthday;charaddr[30];}student1,student2; //声明一个结构体类型Date//声明一个结构体类型Student//定义student1和student2为结构体类型Student的变量 Student的结构如图5-2所示。
Studentnumname[20]sexage birthday addr[30] monthdayyear 图5-2含有结构体类型成员birthday的Student结构体
(5)结构体中的成员名可以与程序中的变量名相同,但二者没有关系。
例如,程序中可以另定义一个整型变量num,它与student中的num是两回事,互不影响。
5.1.3结构体变量的引用和初始化
1.结构体变量的引用定义了结构体变量之后,可以引用这个变量。
对于非指针型结构体变量,是通过成员运算符“.”,逐个访问其成员,一般格式为: 结构体变量名.成员名 例如,可以这样对变量的成员赋值: student1.num=0105101201; 对于指针型结构体变量,是通过“->”运算符来访问其成员,当然也可以用“.”运算
符来访问,一般格式为: 指针->成员名或 *(指针).成员名 例如,定义Student*student3=&student1;之后,可以这样对变量的成员赋值: 第5章自定义数据类型149 (student3).num=0105101201;或student3->num=0105101201; 注意:在利用指针引用结构体成员时,-和>之间不能有空格。
如果结构体成员本身又是一个结构体类型,则只能通过多级的分量运算,对最低一级的成员进行引用,此时,引用的格式扩展为: 结构体变量名.成员名.子成员名.….最低级子成员名 例如,对变量student1的成员day赋值: student1.birthday.day=23; “.”和“->”是结构体成员引用运算符,是优先级最高的运算符,具有左结合性。
对于结构体变量的引用,有以下几点说明。
(1)可以将一个已赋值的结构体变量的值赋给另一个具有相同结构的结构体变量。
方法是: 结构体变量名1=结构体变量名2; 如上面的student1和student2都是Student类型的变量,当student1已有值时,可以这样赋值: student2=student1;
(2)对结构体成员变量的使用可像普通变量那样进行,如进行赋值,参加运算等。
如: Studentstudent1,student2;student1.num=9901;student2.num=s1.num+1;student1.age++; 由于“.”运算符的优先级最高,student1.age++相当于(student1.age)++。
++是对student1.age进行自加运算,而不是先对age进行自加运算。
(3)不能整体引用一个结构体变量,将结构体变量作为一个整体进行输入和输出。
例如,已定义student1和student2为结构体变量,并且它们已有值。
不能企图这样输 出结构体变量中的各成员的值: cin>>student1; 只能对结构体变量中的各个成员分别进行输入和输出。
(4)可以引用结构体变量成员的地址,也可以引用结构体变量的地址。
如: cout<<&student1;cout<<&student1.age; //输出student1的首地址//输出student1.age的地址 结构体变量的地址主要用作函数参数,将结构体变量的地址传递给形参。
2.结构体变量的初始化结构体变量的初始化就是在定义结构体变量的同时,为其成员提供初值,可采用下述方法。
(1)在定义结构体类型的同时,为结构体变量初始化。
150C++程序设计简明综合教程 struct结构体名{ 成员列表}变量名={初始值列表}; 例如: structStudent{ intnum;charname[20];charsex;intage;Datebirthday;charaddr[30];}student1={0105101201,"ZhangShang",'M',19,5,17,1993,"ChangSha"}; 上述变量student1中的数据及其所占内存空间情况如图5-3所示。
student1.numstudent1.namestudent1.sexstudent1.agestudent1.birthday.monthstudent1.birthday.daystudent1.birthday.yearstudent1.addr 0105101201ZhangShangM195171993ChangSha 4字节20字节1字节4字节4字节4字节4字节30字节 12字节 图5-3变量student1数据分配图 注意:赋初值时,{}中间的数据顺序必须与结构体成员的定义顺序一致,否则就会出现混乱。
(2)利用已有的结构体类型定义结构体变量,并同时初始化。
即采取声明类型与定义变量分开的形式,在定义变量时进行初始化。
格式是: 结构体名称变量名={值
1,值2,…}; 如: Studentstudent1={0105101201,"ZhangShang",'M',19,5,17,1993,"ChangSha"}; 此时,student1变量中的num成员的值为0105101201,name成员的值为"ZhangShang",sex成员的值为'M',等等。
(3)结构体变量在程序中赋值。
如果定义结构体变量时未对其赋初值,那么在程序中只能一个一个地对其成员逐一赋 值,或者用已赋值的同类型的结构体变量对其赋值。
【例5-1】引用结构体变量中的成员。
第5章自定义数据类型151 #include
#include
结构体数组与数值型数组的不同之处在于:每个数组元素都是一个结构体类型的数据,它们都分别包括各个成员项。
1.结构体数组的定义定义结构体数组和定义结构体变量的方法相仿,定义结构体数组时只需声明其为数组即可。
如: structStudent{ intnum;charname[20];charsex;intage;floatscore;charaddr[30];};Studentstu[3]; //声明结构体类型Student//定义Student类型的数组stu 可以直接定义一个结构体数组,如: structStudent{ intnum;charname[20]; 第5章自定义数据类型153 charsex;intage;floatscore;charaddr[30];}stu[3]; 或 struct{ intnum;charname[20];charsex;intage;floatscore;charaddr[30];}stu[3];
2.结构体数组初始化结构体数组的初始化与其他类型的数组一样,对结构体数组可以初始化。
如: structStudent{ intnum;charname[20];charsex;intage;floatscore;charaddr[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[]={{…},{…},{…}}; 编译时,系统会根据给出初值的结构体常量的个数来确定数组元素的个数。
数组的初始化也可以用以下形式。
Studentstu[]={{…},{…},{…}};//已事先声明了结构体类型Student 由上可以看到,结构体数组初始化的一般形式是: 结构体名数组名[]={初值表列}; 注意:一个结构体常量应包括结构体中全部成员的值。
3.结构体数组的引用下面举一个简单的例子来说明结构体数组的定义和引用。
154C++程序设计简明综合教程 【例5-3】统计候选人得票数目的程序。
设有三个候选人,最终只能有一人当选为领导。
今有10个人参加投票,从键盘先后输入这10个人所投的候选人的名字,要求最后输出这三个候选人的得票结果。
可以定义一个候选人结构体数组,包括三个元素,在每个元素中存放一个候选人的数据。
程序如下。
#include
0,"Zhang",
0,"Fun",0}; //定义Person类型的数组,内容为三个候选人的姓名和当前的得票数 inti,j; charleader_name[20]; //leader_name为投票人所选的人的姓名 for(i=0;i<10;i++) { cin>>leader_name; //先后输入10张票上所写的姓名 for(j=0;j<3;j++) //将票上姓名与三个候选人的姓名比较 if(strcmp(leader_name,leader[j].name)==0) leader[j].count++;
//如果与某一候选人的姓名相同,就给他加一票 } cout<
在定义数组时使之初始化,使三位候选人的票数都先 置零。
在这个例子中,也可以不用字符数组而用string方法的字符串变量来存放姓名数据, 程序可修改如下。
#include
0,"Zhang",
0,"Fun",0}; inti,j; stringleader_name; //leader_name为字符串变量 for(i=0;i<10;i++) { cin>>leader_name; for(j=0;j<3;j++) if(leader_name==leader[j].name)
//直接用"=="进行比较 leader[j].count++; } cout<
可以设一个指针变量,用来指向一个结构体变量,此时该指针变量的值是结构体变量的起始地址。
指针变量也可以用来指向结构体数组中的元素。
1.指向结构体变量的指针结构体变量的指针:是指结构体变量所占内存单元的起始地址。
因此,可以定义指针变量指向结构体变量。
此时该指针变量的值就是结构体变量在内存中的起始地址。
【例5-4】使用指向结构体变量的指针。
#include
1 lilinM 89
1 lilinM 89
1 lilinM 89 可见,三种访问结构体变量各成员的值的结果完全相同。
说明:
(1)例5-4中(*p).num也可写为p->num。
(2)结构体取成员的运算可以采用以下三种形式。
①结构体变量名.成员名②(*结构体指针变量名).成员名③结构体指针变量名->成员名
2.指向结构体数组元素的指针如果结构体变量的指针初始化为指向结构体数组的首元素,则该指针变量的值就是结构体数组在内存中的起始地址;然后通过指针变量的自增运算可以依次指向数组每个元素。
【例5-5】使用指向结构体数组元素的指针。
#include
1,"lilin",'M',18},{
2,"sum",'M',19},{
3,"zhao",'M',20}};voidmain() 第5章自定义数据类型157 {student*p; for(p=stu;p
2 sum
M 19
3 zhaoM 20 说明:把stu赋给指针变量p,就表示p指向了该数组的首元素(p的值为数组的起始地址)。
p++表示p指向数组的下一个元素,利用这种方法可以访问数组中所有元素的值。
5.1.6结构体与函数 与int、char、float、double等类型一样,结构体变量可作为函数的参数和函数的返回值。
1.结构体类型作为函数参数结构体类型作为函数的参数有三种形式:传值、传址和引用。
1)用结构体类型的变量作函数的参数(传值)在C++语言中,允许把结构体类型的变量直接作为函数的参数,但要注意:此时主调函数的调用点上的实参与被调函数相应位置上的形参必须是相同的结构体类型,是将实参的值(各成员的值)传递给相应的形参。
【例5-6】用结构体类型的变量作为函数的参数。
#include
1,"lilin",'M',18},{
2,"sum",'M',19},'M',20}};voidmain(){inti;for(i=0;i<3;i++) print(stu[i]); {
3,"zhao", 158C++程序设计简明综合教程 }voidprint(students){ cout<
2 sum
M 19
3 zhaoM 20 可见,当把一个结构体类型的变量作为函数的参数时,可以将该变量的值(包含结构体类型中各成员的值)传递给被调函数的形参。
2)用指向结构体的指针作函数的参数(传址)用指向结构体变量的指针作参数,这种方式同指针作为函数的参数的原理一致,传递的是结构体变量的地址(指针)。
【例5-7】用指针的方法实现例5-6程序的功能。
#include"iostream"usingnamespacestd;structstudent{intnum;charname[20];charsex;intage;};voidprint(student*);studentstu[3]={{
1,"lilin",'M',18},{
2,"sum",'M',19},{
3,"zhao",'M',20}};voidmain(void){ inti;for(i=0;i<3;i++) print(&stu[i]);}voidprint(student*s){cout<
5-8】用结构体变量引用的方法实现例5-7程序的功能。
#include
1,"lilin",'M',18},{
2,"sum",'M',19},{
3,"zhao",'M',20}};voidmain(){inti;for(i=0;i<3;i++)print(stu[i]);}voidprint(student&s){ cout<
返回结构类型值的函数 的定义格式如下。
结构体名称{ 函数体} 函数名(形参及类型说明) 【例5-9】定义一个返回结构体类型的函数,求所有同学中年龄最大的同学。
#include
1,"lilin",'M',18},{
2,"sum",'M',19},{
3,"zhao",'M',20}};voidmain(){ studentmaxold=max(stu,3);cout<
3.返回结构体引用的函数返回结构体引用的函数与返回其他类型引用的函数的定义方法和原理一致,但注意不要返回一个局部变量的引用即可。
5.2共用体类型 共用体提供了一种可以把几种不同类型的数据存放于同一段内存的机制。
这种使几个不同的变量占用同一段内存空间的结构称为共用体。
共用体成员互相覆盖,不能同时引用。
5.2.1共用体类型与变量的定义 有时需要使几种不同类型的变量存放到同一段内存单元中。
例如,可把一个整型变量、一个字符型变量、一个单精度型变量放在同一个地址开始的内存单元中(如图5-4所示)。
地址1000100110021003 字符变量整型变量 单精度变量 图5-4将几种不同类型的变量存放到同一段内存单元中 从图5-4看出,以上三个变量在内存中占的字节数不同,但都从同一地址开始存放。
也就是使用了覆盖技术,几个变量互相覆盖。
这种使几个不同的变量共占同一段内存的结构,称为共用体(union)类型的结构(有些书中译为联合)。
声明共用体类型的一般形式为: union共用体类型名{ 第5章自定义数据类型161 成员表列}; 如: unionData{ charch;inti;floatf;}; 共用体Data包含三个成员,它们使用同一地址的内存,共用体的大小是成员中占内存最大的成员的大小。
某个时刻只能存放某一成员,不同时刻存放的成员可能不同。
与结构体变量定义类似,可以声明共用体之后,再定义共用体变量;也可在声明共用体类型的同时定义共用体变量;也可没有共用体类型名而直接定义共用体变量。
声明共用体类型后,定义共用体变量的一般形式为: 共用体类型名共用体变量名; “共用体”与“结构体”的定义形式相似。
但它们的含义是不同的。
结构体变量所占内存长度是各成员占的内存长度之和。
每个成员分别占有其自己的内存单元。
共用体变量所占的内存长度等于最长的成员的长度。
5.2.2共用体变量的引用 对共用体成员的引用与对结构体成员的引用相同,如果通过共用体变量来引用成员,使用“.”,如果通过共用体指针来引用成员,则要使用“->”或“.”。
如:Dataa,*p;例如,下面的引用方式是正确的。
a.i,*p->i,(*p).i(引用共用体变量中的整型成员i)a.ch,*p->ch,(*p).ch(引用共用体变量中的字符型成员ch)a.f,*p->f,(*p).f(引用共用体变量中的双精度型成员d)不能只引用共用体变量,例如: cout<几点说明:
(1)使用共用体变量的目的是希望用同一个内存段存放几种不同类型的数据。
但请注意:在每一瞬时只能存放其中一种,而不是同时存放几种。
(2)能够访问的是共用体变量中最后一次被赋值的成员,在对一个新的成员赋值后原 162C++程序设计简明综合教程 有的成员就失去作用。
(3)共用体变量的地址和它的各成员的地址都是同一地址。
(4)不能对共用体变量名赋值;不能企图引用变量名来得到一个值;不能在定义共用 体变量时对它初始化;不能用共用体变量名作为函数参数。
5.2.3共用体与结构体的联合使用 共用体类型可以出现在结构体类型声明中,也可定义共用体数组。
反之,结构体也可出现在共用体类型定义中,数组也可成为共用体成员。
【例5-10】设有若干个人员的数据,其中有学生和教师。
学生的数据中包括:姓名、号码、性别、职业、年级。
教师的数据中包括:姓名、号码、性别、职业、职务。
可以看出,学生和教师所包含的数据是不同的。
现要求把它们放在同一表格中,如果job项为s(学生),则第5项为grade(年级)。
Li是3年级的。
如果job项是t(教师),则第5项为position(职务)。
Wang是prof(教授)。
显然对第5项可以用共用体来处理(将class和position放在同一段内存中)。
要求输入人员的数据,然后再输出。
为简化起见,只设两个人(一个学生、一个教师)。
程序如下。
#include
5章自定义数据类型163 if(person[i].job=='t')cin>>person[i].category.position;//若是教师则输入职务 }cout<
如果一个变量只有几种可能的值,可以定义为枚举(enumeration)类型。
而“枚举”就是将变量的值一一列举出来,变量的取值只能在列举出来的值的范围内。
声明枚举类型用enum开头。
例如: enumweekday{sun,mon,tue,wed,thu,fri,sat}; 上面声明了一个枚举类型weekday,花括号中sun,mon,…,sat等称为枚举元素或枚举常量。
表示这个类型的变量值只能是以上7个值之
一。
枚举元素是用户自己定义的标识符。
声明枚举类型的一般形式为: enum枚举类型名{ 枚举常量标识符[=整型常数],枚举常量标识符[=整型常数],…枚举常量标识符[=整型常数],}; 在声明了枚举类型之后,可以用它来定义变量。
其一般形式为: [enum]枚举类型名枚举变量列表; 164C++程序设计简明综合教程 在C++中枚举类型名可包含关键字enum,也允许不写enum,一般也不写enum。
如: weekdayworkday,week_end; 这样,workday和week_end被定义为枚举类型weekday的变量。
根据以上对枚举类型weekday的声明,枚举变量的值只能是sun到sat之
一。
例如: workday=tue;week_end=sat;//这两句都是正确的 与结构体变量和共用体变量定义类似,也可以直接定义枚举变量,如: enum{sun,mon,tue,wed,thu,fri,sat}workday,week_end; 注意:这些标识符(枚举元素)并不自动地代表什么含义。
对枚举类型的几点说明:
(1)在C++中,枚举元素是作常量处理的,所以枚举元素不是变量,不能对它们赋值,如:sun=0;mon=1;不对。
(2)枚举元素作为常量,它们是有值的,如果枚举常量标识符省略“=整型常数”,则C++按定义时的顺序对它们赋值为0、1、2、
3、…,但当枚举常量标识符中某个成员赋值后,其后的成员则按此数值依次加1作为其序列号。
若有定义:enumWeekday{sun,mon,tue,wen,thu,fri,sat}workday;,则枚举元素sun的值为0,mon元素的值为
1,…,sat元素的值为
6。
若有定义:enumWeekday{mon=1,tue,wen,thu,fri,sat,sun}workday;,则枚举元素mon元素的值为
1,…,sun元素的值为
7。
如果有赋值语句:workday=thu;,则说明变量workday的值为
4。
(3)枚举值可以用来做判断比较。
如: if(workday
如:workday=4;,这是错误的,因为workday与4不是同一数据类型,应进行类型的强制转换之后才能赋值。
如: workday=(Weekday)4; 上述语句是将序号为4的枚举元素赋给worday,等价于: workday=thu; 【例5-11】口袋中有红、黄、蓝、白、黑5种颜色的球若干个。
每次从口袋中任意取出三个球,问得到三种不同颜色的球的可能取法,输出每种排列的情况。
#include
=j) //若前两个球的颜色不同 { for(k=red;k<=black;k++) //只有前两个球的颜色不同,才需要检查第三个球的颜色 if((k!
=i)&&(k!
=j)) //三个球的颜色都不同 { n=n+1; //使累计值n加
1 cout<
3 { case1:pri=color(i);break; //color(i)是强制类型转换,使pri的值为i case2:pri=color(j);break;//使pri的值为j case3:pri=color(k);break;//使pri的值为k default:break; }switch(pri) //判断pri的值,输出相应的"颜色" { casered:cout<
5.4类型定义 在C++语言中,除了可以直接用C++提供的标准类型名int、char、floot、double、long 166C++程序设计简明综合教程 等直接声明结构体、共用体、枚举等类型外,还可以用typedef定义新的类型名代替已有的类型名。
用typedef定义新的类型名的格式为: typedef类型名标识符 如: typedefintINTEGER;typedeffloatREAL; //指定用标识符INTEGER代表int类型//指定用REAL代表float类型 这样,以下两行等价: inti,j;floata,b;INTEGERi,j;REALa,b; 这样可以使熟悉其他高级语言的人能用INTEGER和REAL定义变量,以适应他们在其他高级语言学习中形成的习惯。
如果在一个程序中,整型变量是专门用来计数的,可以用COUNT来作为整型类型名: typedefintCOUNT; //指定用COUNT代表int型 COUNTi,j; //将变量i,j定义为COUNT类型,即int类型 在程序中将变量i、j定义为COUNT类型,可以使人们更一目了然地知道它们是用于 计数的。
也可以声明构造类型,其格式为: typedef构造类型{ 成员表列;}标识符; //如struct,若在构造类型之前用了关键字typedef,表示是声明新名//注意标识符是新类型名,而不是变量名 如: typedefstruct{intyear,month,day;}DATE; 所声明的新类型名DATE代表上面指定的一个结构体类型。
这样就可以用DATE定义变量: DATEbirthday;DATE*p; //p为指向此结构体类型数据的指针 还可以进一步列出typedef的一些使用形式。
(1)用typedef为某一数组类型指定新关键字。
typedefcharSTRING[81];//声明STRING为字符数组类型,包含81个元素 STRINGstr1; //等价于charstr1[81]; typedefintNUM[100];//声明NUM为整型数组类型,包含100个元素 第5章自定义数据类型167 NUMn; //定义n为包含100个整型元素的数组,等价于intn[100];
(2)用typedef为某一指向函数的指针类型指定新关键字。
typedef(int*)(*P_Fun)(int,int);//P_Fun为指向函数的指针类型(函数的返回值为int*,带两个int参数) P_Funpfun[5];//pfun为长度为5的函数指针数组 等价于: int*(*pfun[5])(int,int); 归纳起来,声明一个新的类型名的方法如下。
(1)先按定义变量的方法写出定义语句(如inti;)。
(2)将变量名换成新类型名(如将i换成COUNT)。
(3)在最前面加typedef(如typedefintCOUNT)。
(4)然后可以用新类型名去定义变量。
再以声明上述的数组类型为例来说明。
(1)先按定义数组形式书写:intn[100];。
(2)然后将变量名n换成自己指定的类型名: intNUM[100];
(3)再在前面加上typedef,得到 typedefintNUM[100];
(4)最后就可以用新类型名来定义变量:NUMn;(n是包含100个整型元素的数组)。
习惯上常把用typedef声明的类型名用大写字母表示,以便与系统提供的标准类型标 识符相区别。
关于typedef有以下几点说明: (1)typedef可以声明各种类型名,但不能用来定义变量。
用typedef可以声明数组类型、字符串类型,使用比较方便。
(2)用typedef只是对已经存在的类型增加一个类型名,而没有创造新的类型。
(3)当在不同源文件中用到同一类型数据(尤其是像数组、指针、结构体、共用体等类型数据)时,常用typedef声明一些数据类型,把它们单独放在一个头文件中,然后在需要用到它们的文件中用#include命令把它们包含进来,以提高编程效率。
(4)使用typedef有利于程序的通用与移植。
有时程序会依赖于硬件特性,用typedef便于移植。
习题
5 一、选择题
1.有以下程序段 168C++程序设计简明综合教程 structst{intx;int*y; }*pt;inta[]={1,2},b[]={3,4};stc[2]={10,a,20,b};pt=c; 以下选项中表达式的值为11的是(
A.*pt->y B.pt->x
2.有以下说明和定义语句 )。
C.++pt->x
D.(pt++)->x structstudent{intage;charnum[8];};studentstu[3]={{20,"200401"},{21,"200402"},{19,"200403"}};student*p=stu; 以下选项中引用结构体变量成员的表达式错误的是()。
A.(p++)->num B.p->num
C.(*p).num
3.有以下程序 D.stu[3].age #include
) A.13 B.14 C.208 D.209
4.设有如下枚举类型定义 enumlanguage{Basic=
3,Assembly=
6,Ada=100,COBOL,Fortran}; 枚举量Fortran的值为()。
A.
4 B.
7 5.以下叙述中错误的是()。
C.102 D.103 第5章自定义数据类型169
A.可以通过typedef增加新的类型
B.可以用typedef将已存在的类型用一个新的名字来代表
C.用typedef定义新的类型名后,原有类型名仍有效
D.用typedef可以为各种类型起别名,但不能为变量起别名
二、填空题
1.若有以下定义和语句,则sizeof(a)的值是 ,而sizeof(b)的值是 。
可用a.day引用结构体成员day,请写出引用结构体成员a.day的其他两种形 式 、 。
struct{ intday;charmouth;intyear;}a,*b;b=&a;
2.设有以下结构类型说明和变量定义,则变量a在内存所占字节数是 。
structstud { charnum[6]; int s[4]; doubleave; }a,*p;
3.若有以下说明和定义语句,则变量w在内存中所占的字节数是 。
unionaa{floatx,y;charc[6];};structst{unionaav;floatw[5];doubleave;}w;
4.下面程序的输出是 。
#include
#include
#include
(1) ;
(2) ; for(i=1;i
5章自定义数据类型171 }
(5) ; }
三、编程题
1.编程:Input和Output函数输入/输出5个学生的数据记录,其中每个学生包含学号、姓名、5门课程分数的信息。
2.使用结构类型表示复数,编程输入两个复数,可以选择进行复数的+、-、×和÷运算,并输出结果。
3.将一个班的学生姓名和成绩输入到结构体数组中,寻找并输出最高分的学生。
4.使用X-Y平面直角坐标系上的点,编写程序读入一个四边形,判别由这4个点的连线构成的图形是否为正方形、矩形或其他四边形。
(要求:定义求两点距离的函数使用结构参数)。
声明:
该资讯来自于互联网网友发布,如有侵犯您的权益请联系我们。