第八章结构体联合与枚举.docx
- 文档编号:24146997
- 上传时间:2023-05-24
- 格式:DOCX
- 页数:61
- 大小:226.18KB
第八章结构体联合与枚举.docx
《第八章结构体联合与枚举.docx》由会员分享,可在线阅读,更多相关《第八章结构体联合与枚举.docx(61页珍藏版)》请在冰豆网上搜索。
第八章结构体联合与枚举
第八章结构体、联合与枚举
8.1概述
到目前为止,我们已经学过的数据类型有整型、字符型和实型等基本类型,也学过了数组——一种构造类型数据,数组中的各个元素属于同一种类型。
但在实际应用中,一组数据并不是都由相同性质的数据构成,而常常是由一些不同类型的数据构成。
譬如要统计某班学生成绩,需要知道学生的姓名、成绩,姓名与成绩是两种不同类型的数据。
又如通讯录中有姓名、地址、邮政编码、电话等不同类型的数据。
若用数组来解决问题,则会将这些互相联系的数据割裂开来,不便于引用。
为此,C语言允许用户根据需要自行构造出一种被称为“结构体”的数据类型。
结构体是一种较为复杂而又非常灵活的构造型的数据类型。
一个结构体类型的数据可以由若干个称为成员(或域)的成分组成。
不同的结构体类型其成员不同。
对于一个具体的结构体而言,其成员的数量是固定的,这一点与数组相同,但该结构体中各成员的数据类型可以不同,这是结构体与数组的重要区别。
8.2结构体类型定义
结构体类型定义的一般形式为:
struct结构体名
{类型名1结构成员名1;
类型名2结构成员名2;
……
类型名n结构成员名n;
};
其中:
⑴struct是关键字,是定义结构体类型的标志。
不能省略,表示这是一个“结构体”类型。
⑵结构体名是用户定义的结构体类型名,其命名规则同变量名。
⑶类型名1~n说明了结构成员的类型。
⑷结构成员名1~n为用户定义的一个或多个结构成员的名称,其命名规则同变量名。
多个同类型的成员彼此用逗号分隔。
通常将上面定义形式中{}中的部分称之为成员表列,也叫域表。
注意:
结构体类型的声明是以分号(;)结尾的。
例如:
structstudent
{
intnum;
charname[20];
charsex;
intage;
floatscore[4];
};
表明定义了一个structstudent结构体类型,成员有num,name,score等。
structstudent和系统已定义了的标准类型(如int、float、char、double等)一样可以用来作为定义变量的类型。
关于结构体类型有几点说明:
⑴结构体类型定义是由程序员根据设计需要自行定义的,因此结构体类型可以有多种,每种结构体类型都可以有自己的结构体名以及包含不同数目的成员。
⑵若定义了一个结构体类型,那仅仅是定义类型而已,而不分配内存单元。
例如上面已经定义了的structstudent结构体类型,structstudent可以用来定义一个该类型的变量,并不意味着它的那些成员被分配了内存空间。
⑶结构体成员类型可以是整型、实型、字符型、数组、指针等基本类型或构造类型,还可以是已定义过的结构体类型。
例如可以定义一个structdate结构体类型,然后重新定义如下形式的structstudent结构体类型:
structdate
{
intyear;
intmonth;
intday;
};
structstudent
{
intnum;
charname[20];
charsex;
structdatebirthday;
floatscore[4];
};
其中structstudent中的成员birthday是另一种结构体类型structdate,我们称这种情况为结构体嵌套。
⑷成员名可以与程序中的变量名相同,两者不代表同一对象。
例如,程序中可以另定义变量num,它与structstudent中的num是两回事,互不干扰。
8.3结构体变量
前一章节已经声明:
结构体类型的定义只是指出了该结构的组成情况,表明存在有此种类型的结构模型。
该结构体类型中不能存放具体的数据,系统也不会为它分配实际的存贮单元。
为了能在程序中使用结构体类型的数据,应在定义了某种结构体类型以后,再定义该结构体类型的变量,以便在结构体类型的变量中存放具体的数据。
8.3.1结构体变量的定义
结构体变量的定义有以下三种形式:
1.先声明结构体类型,再定义结构体类型的变量。
若事先已声明了结构体类型,那么只需再用下面格式定义结构体变量即可。
结构体类型名结构体变量名;
例如在前面我们定义结构体类型date、student后,就可以定义该类型的结构体变量。
即:
structdater1,r2,r3;
structstudentst1,st2;
在此,structdate、structstudent分别表示的是结构体类型名,r1、r2、r3分别表示的是类型为structdate的结构体变量,st1、st2分别表示的是类型为structstudent的结构体变量。
定义了结构体变量以后,系统自动为结构体变量分配存贮单元。
如r1、r2、r3在内存中分别各占6个字节,st1、st2在内存中各分别占个45字节。
structstudent类型的变量在内存中的存储结构如图8-1所示,其中表格下的数字表示上面的变量占用的字节数。
num
name
sex
Birthday
score[0]
score[1]
score[2]
score[3]
year
month
Day
2
20
1
2
2
2
4
4
4
4
图8-1structsutdent型变量内存结构
可以用sizeof运算符来计算一个结构体数据的长度,一般形式为:
sizeof(数据类型或变量名)
如:
sizeof(structstudent)或sizeof(st1)
它们的结果为45。
2.在声明结构体类型的同时定义结构体类型的变量。
例如:
structstudent
{
intnum;
charname[20];
charsex;
intage;
floatscore[4];
}st1,st2;
它的作用与前面定义的相同。
即定义了两个structstudent类型的变量st1,st2。
这种形式定义的一般格式为:
struct结构体名
{成员表列}变量表列;
3.直接定义结构体类型变量。
在声明结构体类型时可以直接定义结构体变量,其定义格式为:
struct
{
成员表列
}结构体变量名表;
例如:
struct
{
intnum;
charname[20];
charsex;
intage;
floatscore[4];
}st1,st2;
即在关键字struct后省略了结构体名。
8.3.2结构体变量的引用
定义了结构体变量后,我们就要在程序中使用它,也就是说要引用该变量。
引用结构体变量多数情况是引用结构体成员,如果要将结构体变量整体引用则往往只限于将一个结构体变量直接赋值给另一个具有相同类型的结构体变量。
例如有声明语句:
structstudentstux,stuy;,执行stuy=stux;语句后变量stuy中各成员的值都完全与stux各成员的值相等。
那么如何引用结构体变量的成员呢?
对结构体成员的引用方式为:
结构体变量名.成员名
其中,“.”为结构体成员运算符,它的优先级处于所有运算符优先级的最高级别。
例如st1.num表示st1变量中的num成员,可以对它赋值st1.num=1001,这时st1.num就相当于一个整型数。
引用结构体变量时应注意如下规则:
⑴不能将结构体变量作为一个整体进行输入和输出。
如已定义structstudentst1,以下两句对结构体变量进行输入输出的语句是错误的。
scanf("%d%s%c%d%d%d%f%f%f%f",&st1);
printf("%d,%s,%c,%d,%d,%d,%.2f,%.2f,%.2f,%.2f\n",st1);
但可逐个全部或部分输入输出。
如:
scanf("%d%c%f",&st1.num,&st1.sex,&st1.score[2]);
printf("%d,%c,%.2f\n",st1.num,st1.sex,st1.score[2]);
⑵若成员本身又是一个结构体类型,则必须逐层使用成员名定位,找到最底层的成员。
例如在结构体变量st1中对成员year的引用方式为:
st1.birthday.year。
⑶若结构体中的成员是字符型数组时,则可将其看作是“字符串变量”,而直接引用。
例如:
对st1中name的引用可写成:
st1.name。
⑷若结构体中的成员是数值型数组时,则对该数组成员的引用,应该为对该数组元素的引用。
例如:
对st1中score数组元素的引用可写成:
st1.score[0],st1.score[1],st1.score[2],st1.score[3]。
实际上,我们可以将结构体中变量的每个成员当作是同类型的普通变量,对它进行同类变量所允许的任何操作。
例如成员变量st1.name是字符串,可以对它进行字符串变量所允许的任何操作,包括输入、输出。
同样对于成员中的数组元素也可按同类型的数组元素进行操作。
如:
scanf("%s",st1.name);/*对结构体成员name赋值*/
for(i=0;i<4;i++)
scanf("%f",&st1.score[i]);/*对结构体数组成员score赋值*/
st1.num++;/*结构体成员num自增运算*/
st1.sex=getchar();/*对结构体成员sex进行赋值*/
st1.brithday.year=1962;
特别提醒,在某些编译系统比如TC2.0,scanf("%f",&st.score[i]);这样的给实变量的赋值语句用在循环结构中时,实型变量可能不能被正确赋值,我们可以采用“曲线救国”的方法来操作,比如先定义一个实型变量f,先用scanf()语句给f赋值,然后再把它赋给结构体的实型成员变量。
例如:
for(i=0;i<4;i++)
scanf("%f",&st1.score[i]);/*对结构体数组成员score赋值*/
定义变量f后,按上面的方法可写成:
for(i=0;i<4;i++)
{scanf("%f",&f);st1.score[i]=f;}
读者可以看到后面的章节中给结构体的实型成员赋值时会出现类似的语句。
8.3.3结构体变量初始化
初始化就是在定义变量的同时给变量赋初值,例如:
structstudent
{
intnum;
charname[8],sex;
structdate{intyear,month,day;}birthday;
floatscore[4];
}st1={101,"wang",'M',1975,8,7,83.5,88,75.5,90};
初始化后,变量st1的内容如图8-2所示:
birthday
num
name
sex
year
month
day
score[0]
score[1]
score[2]
score[3]
101
wang
M
1975
8
7
83.5
88
75.5
90
图8-2结构体变量成员初始化后存储结构图
在对结构体变量进行初始化时,系统是按每个成员在结构体中的顺序一一对应赋初值的。
若只对部分成员进行初始化,则只能给前面的若干成员赋值,而不允许跳过前面的成员给后面的成员赋值。
例如:
structstudent
{
intnum;
charname[8],sex;
structdate{intyear,month,day;}birthday;
floatscore[4];
}st1={123,"gao"};
初始化后,变量st1的内容如图8-3所示:
birthday
num
name
sex
year
month
day
score[0]
score[1]
score[2]
score[3]
123
gao
\0
0
0
0
0
0
0
0
图8-3结构体变量部分成员赋值后存储结构图
由图知,对未进行初始化的成员,若为数值型数据,系统自动赋初值为0;若为字符型数据,系统自动赋初值'\0'。
另外,也可以用scanf()函数从键盘输入数据给结构体变量赋初值。
【例8.1】定义结构体变量st,从键盘给它赋初值并输出结果。
structstudent
{
intnum;
charname[20];
charsex;
intage;
floatscore;
};
main()
{
structstudentst;
printf("pleaseinputdata:
\n");
scanf("%d%s%c%d%f",&st.num,st.name,&st.sex,&st.age,&st.score);
printf("%d%s%c%d%.1f",st.num,st.name,st.sex,st.age,st.score);
}
注意,上例中的scanf语句中st.name前不需要加地址符&,因为st.name本身是个字符数组名,已经代表了地址的含义。
8.4结构体数组
8.4.1结构体数组的定义
跟简单变量一样,一个结构体变量一次只能存放一组数据如某一个学生的信息,若有某一个班级的学生信息需要存放、运算,这时就应该使用数组了,也就是结构体数组。
结构体数组是同类型结构体变量的集合。
结构体数组定义的和结构体变量定义方式基本相同,只需在原变量名说明的后面加上一对方括号,方括号中指明数组元素的个数,而此时变量名就是结构体数组名。
例如:
structstudent
{
intnum;
charname[8],sex;
intage;
floatscore;
};
structstudentstu[3];
以上定义了一个类型为structstudent结构体数组stu,该数组共有3个元素,它们在内存中连续存放。
跟定义结构体变量一样,结构体数组也可以直接定义:
structstudent
{
intnum;
charname[8],sex;
intage;
floatscore;
}structstudentstu[3];
或将上面structstudent中的student省略。
8.4.2结构体数组初始化与引用
结构体数组的引用方式跟结构体变量基本相同,同样需要用引用符号“.”,唯一的区别在于引用结构体数组时是引用每个结构体数组元素的成员,如沿用8.4.1中的数组定义,则stu[i].score表示数组stu中下标为i的元素中的“分数”成员score,这里i的取值范围从0到2。
结构体数组的初始化方式跟基本类型的变量数组初始化方式相似,但由于此时数组元素是结构体类型,它包含有一个或多个成员,因此在赋初值时需要用两对花括号{}配套使用。
沿用8.4.1中的数组定义,对stu初始化赋值如下:
structstudentstu[3]={{101,"lili",'F',21,89},{102,"fangfang",'M',22,74},{103,"yuanyuan",'F',20,85}};
初始化过的结构体数组stu在内存中的存储结构可用图8-4所示。
num
name
sex
age
score
stu[0]:
101
"lili"
'F'
21
89
stu[1]:
102
"fangfang"
'M'
22
74
stu[2]:
103
"yuanyuan"
'F'
20
85
图8-4结构体数组存储示意图
8.4.3结构体数组的输入和输出
对结构体数组数据的输入或输出可利用for循环结构,例如:
inti;
for(i=0;i<2;i++)
scanf("%d%s%c%d%f",
&stu[i].num,stu[i].name,&stu[i].sex,&stu[i].age,&stu[i].score);
这是给数组stu[3]的元素赋值操作,下面以具体实例来说明结构体数组的输入输出。
【例8.2】写一个程序给5名学生按成绩从低到高排序。
假定学生结构中包含学号、姓名和成绩三种信息。
main()
{
structstudent
{
intnum;
charname[20];
intscore;
};
structstudentst[5],t;
inti,j;
printf("pleaseinputdata:
\n");
for(i=0;i<5;i++)
scanf("%d%s%d",&st[i].num,st[i].name,&st[i].score);
for(i=0;i<4;i++)
for(j=0;j<4-i;j++)
if(st[j].score>st[j+1].score)
{
t=st[j];
st[j]=st[j+1];
st[j+1]=t;
}
printf("sortedresult:
\n");
for(i=0;i<5;i++)
printf("%d%s%d\n",st[i].num,st[i].name,st[i].score);
}
运行结果:
pleaseinputdata:
101zhang87↓
102chen54↓
103li65↓
104zhou99↓
105song75↓
sortedresult:
102chen54
103li65
105song75
101zhang87
104zhou99
8.5指向结构体类型数据的指针
从前面的章节我们了解到,一个变量的指针指变量在内存中所占据的存储区的首地址,可以设一个指针变量指向它,同理,如果已经定义了一个结构体类型的变量,我们也可以设一个指针指向该结构体变量,该指针变量的值即为结构体变量的起始地址。
指针变量还可以用来指向结构体数组中的元素。
8.5.1定义和初始化
定义指向结构体类型的数据指针的方法跟定义指向简单类型变量的指针方法相同,只不过数据类型的声明部分用在程序中已定义的结构体数据类型来替代。
如
structstudent
{
intnum;
charname[20];
floatscore;
};
structstudent*p,stu;
跟任何其他类型的指针一样,要使用定义好的指针变量,在使用前应对它赋值,也就是使它确切地表示某一个变量的地址。
例如有上面的定义,可对p进行如下赋值:
p=&stu;
如果已经定义了结构体数组变量,那么也可以用结构体数组名对同类型的结构体指针赋值。
例如:
structstudentst[5],*p;
p=st;
8.5.2用结构体指针引用结构体成员
在8.3.2节中,我们介绍了“.”-引用符号来引用结构体变量成员,在引入指向结构体变量的指针的概念后,原来引用变量成员的方式可以变为以下形式:
(*指针变量名).成员名
按上节定义的structstudent类型和变量,st.num或(*p).num都表示p所指向的结构体变量st中的成员num。
注意*p两边的括号不可省,因为成员运算符“.”优先于“*”运算符,*p.num就等价于*(p.num),这在逻辑上是错误的。
在C语言中为了使用方便和直观,(*p).num可以改用p->num来代替,“->”称为指向运算符。
因此,下面引用结构体成员的几种方式完全等价:
①结构体变量名.成员名
②结构体指针名->成员名
③(*结构体指针名).成员名
④(&结构体变量名)->成员名
最常用的是①②两种形式。
注意:
“.”只可用在表示变量名含义的变量的后面,而“->”只可用在表示地址含义的变量的后面。
8.5.3指向结构体数组的指针
指向结构体变量的指针除了用于表示单变量的地址外,更经常用于处理结构体数组方面的问题。
下面通过一个简单的例子来说明指针指向结构体数组的应用。
【例8.3】定义一个结构体数组并赋初值,用指针法输出这些数据。
structstudent
{
intnum;
charname[10];
charsex;
};
main()
{
structstudentst[3]={{101,"Xu",'M'},{102,"Yan",'F'},{103,"Ling",'F'}};
structstudent*p;
printf("NoNameSex:
\n");
for(p=st;p printf("%-5d%-7s%c\n",p->num,p->name,p->sex); } 运行结果: NoNameSex: 101XuM 102YanF 103LingF 地址 值 变量 p0→ 0x1000 101 st[0].num 0x1002 Xu st[0].name 0x100C M st[0].sex p1→ 0x100D 102 st[1].num 0x100F Yan st[1].name 0x1019 F st[1].sex p2→ 0x101A 103 st[2].num 0x101C Ling st[2].name 0x1026 F st[2].sex 图8-5指针指向结构体数组示意图 设p是指向上例中structstudent结构体类型的指针变量。 在循环中使p的初值指向st,即数组st的首地址,也就是元素st[0]的地址。 见图8-5中p0的指向。 第一次循环输出st[0]各个成员值后执行p++,p=p+1意味着增加的地址值为结构体类型数组一个元素所占的字节数(本例中为2+10+1=13个字节),此时p指向st[0]的下一个元素st[1],见图8-5中p1的指向。 执行第二次循环输出st[1]各成员的值又执行p++,此时p指向st[2],见图8-5中p2的指向。 然后执行第三次循环输出st[2]各成员值再执行p++,p的值已变成st+3不满足条件跳出循环。 注意: 1.如果p的初值指向stu,即指向第一个元素,则p+1指向下一个元素的起始地址。 例如: (++p)->num先使p自加1,然后得到它指向的元素中的num成员值,即102 (p++)->num先得到p->num的值,即101,然后使p自加1,指向st[1] 2.指针p是指向structstudent类型的数据,它只能表示一个该结构体类型数据的地址,而不能指向一元
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 第八 结构 联合 枚举
