C语言知识第11章王厚财.docx
- 文档编号:4679480
- 上传时间:2022-12-07
- 格式:DOCX
- 页数:18
- 大小:51.56KB
C语言知识第11章王厚财.docx
《C语言知识第11章王厚财.docx》由会员分享,可在线阅读,更多相关《C语言知识第11章王厚财.docx(18页珍藏版)》请在冰豆网上搜索。
C语言知识第11章王厚财
第十一章用户自定义数据类型
前面我们学习了C语言的基本数据类型(整型、实型、字符型),也学习了一种构造数据类型以及指针类型。
本章集中介绍三种构造型数据类型,他们是用户定义类型、结构体和共用体。
11.1用typedef说明新数据类型名
除可直接使用C提供的标准类型和自定义的类型(结构、共用、枚举)外,也可使用typedef定义已有类型的别名。
该别名与标准类型名一样,可用来定义相应的变量。
说明新类型名的语句一般形式:
typedef原类型名新的类型名;
例如:
typedefintINTEEGER;
在这儿原类型名是C提供的数据类型或我们将要学习的结构类型,新的类型名是用户标识符,这条语句的作用相当于为原来的类型取了一个新的名字,并且原有的类型名仍然有效。
在定义变量、数组等时,不仅可以使用原有的类型名,还可以使用新的类型名。
例如:
inta,b[10];与INTEEGERa,b[10];等价
为了与原类型名有所区别,一般新类型名采用大写字母。
还可以这样定义新的数据类型:
typedefintNUM[100];/*说明NUM为含有100个元素的整型数组类型*/
NUMa;/*定义a为整型数组类型*/
归纳起来,定义已有类型别名的方法如下:
(1)按定义变量的方法,写出定义体;
(2)将变量名换成别名;
(3)在定义体最前面加上typedef。
给实型float定义一个别名REAL。
按定义实型变量的方法,写出定义体:
floatf;
将变量名换成别名:
floatREAL;
在定义体最前面加上typedef:
typedeffloatREAL;
说明:
(1)用typedef只是给已有类型取一个别名,并不能创造新的类型。
就如同人一样,除学名外,可以再取一个小名(或雅号),但并不能创造出另一个人来。
(2)typedef与#define有相似之处,但二者是不同的:
前者是由编译器在编译时处理的;后者是由编译预处理器在编译预处理时处理的,而且只能作简单的字符串替换。
11.2结构体
前面我们虽然学习了很多的数据类型,但在实际使用的时候,仅有这些数据类型是不够的。
比如我们标识这样的数据:
新生入学登记表,要记录每个学生的学号,姓名,性别,年龄,身份证号,家庭住址,家庭联系电话等信息。
学号
姓名
性别
年龄
身份证号
家庭住址
家庭联系电话
11301
pin.zhang
F
19
320406*********
changzhou
(0519)8754267
11302
min.li
M
20
612301*********
xi’an
(029)3870909
表示这样的数据我们可以使用数组:
因为有很多学生的信息要处理,按照我们前面学习过的知识,这个任务要使用数组。
但是数组是由相同类型的数据构成。
所以我们只能使用7个单独的数组(学号数组no、姓名数组name、性别数组sex、年龄数组age、身份证号数组pno、家庭住址数组addr、家庭联系电话数组tel)分别保存这几类信息。
分立的几个数组将给数据的处理造成麻烦,我们想若能用一个量来保存一个学生的信息就好了。
那么这个量就有学号、姓名、性别、年龄等组成,并且它们的数据类型又不相同,那么使用我们前面学习过的数据类型就不够了。
这将要用到结构体。
C语言利用结构体将同一个对象的不同类型的属性数据,组成一个有联系的整体。
也就是说可以定义一种结构体类型将属于同一个对象的不同类型的属性数据组合在一起。
本例可以将属于同一个学生的各种不同类型的属性数据(学号、姓名、年龄等)组合在一起,形成整体的结构体类型数据。
从上面可以看出,数组是由相同数据类型的元素组成,而结构体可以由不同的数据类型的数据组成。
结构体是一种构造类型(自定义类型),需要先说明后,才能使用。
11.2.1结构体类型的说明
结构体类型说明的一般形式:
struct结构体名
{
类型1成员1;
类型2成员2;
......
类型n成员n;
};
例如:
structstudent
{
charno[5],name[10],sex;
intage;
charid[20];
charaddr[30];
chartel[10];
};
说明:
(1)结构体名:
结构体类型的名称,遵循标识符规定,在说明结构体时可以省略。
但这种结构体类型将不能被保留下来。
例如:
struct
{
charno[5],name[10],sex;
intage;
charid[20];
charaddr[30];
chartel[10];
};
(2)结构体有若干数据成员,分别有各自的数据类型,结构体成员名同样遵循标识符规定,它隶属于特定的结构体变量(对象),名字可以与程序中其它变量或标识符同名,也可以与其他的结构体类型的成员名相同。
(3)使用结构体类型时,“struct结构体名”作为一个整体,表示名字为“结构体名”的结构体类型,二者都不能省略。
(4)结构体类型的成员可以是基本数据类型,也可以是其它的已经定义的结构体类型(我们称为结构体的嵌套)。
一般结构体成员的类型不能是正在定义的结构体类型(递归定义),但结构体成员若是指针,它的数据类型可以是正在定义的结构体类型。
例如:
我们定义一个date的结构体:
structdate
{
intyear;
intmonth;
intday;
};
structstudent
{
charno[5],name[10],sex;
structdatebirthday;
charid[20];
charaddr[30];
chartel[10];
};
(5)若多个成员的数据类型相同,可以与定义多个相同数据类型的变量类似,放在一行上定义,相同数据类型的成员后面的分号不能缺少。
例如上面定义的date的结构类型,也可以这样定义:
structdate
{
intyear,month,day;
};
(6)结构体类型的说明只是由用户说明了一种新的数据类型,比如上面说明的structstudent、structdate结构体,计算机不为数据类型分配存储空间,就与int等数据类型一样,计算机不为他们分配存储空间,可以为这样数据类型的变量、数组等分配存储空间。
11.2.2结构体类型的变量、指针、数组的定义与赋初值
1.结构体类型变量、指针、数组的定义形式有以下几种:
(1)可以在说明结构体类型的同时定义变量。
structstudent
{
intno;
charname[20];
charsex;intage;
charpno[19];
charaddr[40],tel[20];
}student1,*ps,st[5];/*定义structstudent类型的结构体变量student1、数组st[5]*/
定义了变量student1后,计算机为其分配存储空间,如图11.1:
具有这种结构体类型的变量只能存放一个数据(即一个学生的信息)。
结构体变量中的各成员在内存中按定义的顺序依次存放。
同理,定义了数组st[5]后,计算机就为其分配5个连续的这样的存储单元。
(2)直接定义结构体变量(不给出结构体类型名,匿名的结构体类型)
struct
{
intno;
charname[20];
charsex;intage;
charpno[19];
charaddr[40],tel[20];
}student1,*ps,st[5];
作用与第一种的定义相同,只是省略了结构体名。
(3)先说明结构体类型,再定义结构体变量
前面已说明了structstudent结构体数据类型,可以用它来定义变量:
structstudentstudent1,*ps,stu[5];
这样定义结构体变量,必须struct与student同时使用,不能省略其中的任何一个。
(4)使用typedef给结构体类型定义新的类型名,用新类型名来定义
typedefstructstudent
{
intno;
charname[20];
charsex;intage;
charpno[19];
charaddr[40],tel[20];
}STU;/*给结构体类型定义新的类型名STU*/
直接用STU定义变量与数组:
STUstudent1,*ps,st[5];
对于结构体类型与变量作以下说明:
●结构体类型与变量是不同的概念。
在定义时,一般先说明一个结构体类型,然后再定义该类型的变量;赋值、存取或运算只能对变量,不能对类型;
●编译时只对变量分配空间,对类型不分配空间。
2.结构体变量、数组初始化
和一般的变量、数组一样,结构体类型的变量、数组也可以在定义的同时赋初值。
(1)结构体变量赋初值
在赋值时将初值用一对“{}”括起来,其顺序也应该与结构体中的成员顺序保持一致。
例如:
structstudent
{intno;
charname[20];
charsex;intage;
charpno[19];
charaddr[40],tel[20];
}student1={11301,"pin.zhang",'F',19,320406841001264","changzhou","(0519)8754267"};
本例中,结构体变量student1在定义的同时,其各个成员也按顺序被赋予了相应的一组数据,如图11.2。
对结构体变量赋值时,C编译程序按每个成员再结构体中的顺序一一对应赋初值,不允许跳过前面的成员给后面的成员赋初值;但可以只给前面的成员赋初值,对于后面的未赋值的成员,系统自动赋初值未0。
(2)结构体数组赋初值
给结构体数组赋初值与前面讲过的数组的赋初值相同,只是数组中的每个元素都时一个结构体。
例如:
structbookcard
{
charnum[5];
floatmoney;
}bk[3]={{“no.1”,35.5},{“no.2”,120.34},{“no.3”,123.5}};
与前面讲的基本数据类型的数组的赋初值类似,在定义数组的同时赋初值。
若数组的全部元素的数据都已给出,则数组的大小可以省略。
为数组分配的存储空间如图11.3所示:
也可以先说明结构体,在定义结构体数组的同时赋初值。
例如:
structbookcard
{
charnum[5];
floatmoney;
};
structbookcardbk[3]={{“no.1”,35.5},{“no.2”,120.34},{“no.3”,123.5}};
(3)结构体指针的赋值
结构体类型的指针赋值与其他数据类型的指针的赋值相同,都是将一个地址值赋值给它,只是要注意基类型相同。
例如:
structstudentstudent1,*ps,stu[5];
在以上语句中定义了一个指针ps,ps必须指向类型为structstudent的存储单元。
所以,ps=&student1;或ps=stu都是可以的,第一条赋值语句作用是使指针指向变量student1,第二条赋值语句的作用是使指针指向数组stu的第一个元素。
11.2.3结构体变量中成员的引用
在C中除了可以将一个已有值的结构体变量整体赋给另外一个同类型的结构体变量外,不能对结构体变量做其他整体操作,只能引用结构体变量成员。
像这样的操作是不允许的:
printf(”%d,%c,%c,%d,%c,%c,%c”,student1);
只能输出结构体变量的各个成员的值,来输出整个结构体变量的值。
下面我们介绍一下结构体成员的引用。
1.结构体成员的引用
若已定义了一个结构体类型的变量和基类型与其相同的指针变量,并且该指针变量已指向了此变量,则可以用以下三种形式引用结构体的成员:
●结构体变量名.成员名
●指针变量名->成员名
●(*指针变量名).成员名
其中:
“.”运算符是成员运算符;“->”称为结构体的指向运算符,它由“-”和“>”两部分组成,中间不能加空格。
这两种运算符在C语言中运算的优先级别最高。
例如:
structstudent
{
intno;
charname[20];
charsex;intage;
charpno[19];
charaddr[40],tel[20];
}student1,*ps,st[5];
ps=&student1;
若要引用结构体或数组元素的成员,可以写为:
student1.no通过结构体变量来引用其成员
ps->pno通过指向结构体变量的指针引用其成员
(*ps).no通过指向结构体变量的指针引用其成员
st[2].no引用数组元素的成员
st[0].pno[2]引用数组元素st[0]的成员pno中的下标为2的数据
说明:
(1)对结构体的成员可以象普通变量或数组元素一样进行各种操作或运算
例如:
student1.age=student2.age;
scanf(“%s”,student1.name);
student1.no++;/*由于成员运算符的优先级最高,所以此表达式是对student1.no进行自加运算,student1.no相当于一个变量*/
(2)结构体的成员又是结构体类型时,对成员的访问要使用成员运算符逐级访问。
例如:
student1.birthday.year
student1.birthday.month
(3)既可引用结构体变量成员的地址,也可引用结构体变量的地址。
例如:
scanf(“%d”,student1.no);
ps=&student1;
(4)当使用指针变量引用结构体的成员,并且又与++、――等运算符组成表达式时,应根据运算符的优先级来确定表达式的含义。
例如:
struct
{inta;
char*s;
}x,*p=&x;
且变量x的成员a、s已正确赋值。
则表达式++p->a,使得成员a增1,此表达式等价于++(p->a);而表达式(++p)->a,是使指针p先指向下一个存储单元,然后再取成员a的值;(p++)->a与p++->a等价,表达式的值是指针p所指变量成员a的值,然后指针p增加1。
同理,表达式*p->s,表达式的值是变量x中s指向的存储单元的值;*p->s++是引用了s所指的存储单元之后,指针s加1;表达式(*p->s)++,使得s所指向的存储单元的值增1;而*p++->s在访问了s所指的存储单元之后,使指针p增1。
(5)相同数据类型的结构体变量可以整体赋值
student1=student2;
在这儿,必须保证“=”两边的变量的数据类型要相同。
(若是结构体变量,则属于相同的结构体类型)
例11.1对候选人得票的统计程序。
假设有三个候选人,每次输入一个得票的候选人的名字,要求最后输出各人得票结果。
程序如下:
structperson
{charname[20];
intcount;
}leader[3]={“li”,0,”zhang”,0,”fun”,0};/*为结构体数组赋初值*/
main()
{inti;
charleader_name[20];
scanf(“%s”,leader_name);/*从键盘接受候选人的名字*/
while(len(leader_name)!
=0)/*若没统计完*/
{
for(i=0;i<3;i++)
if(strcmp(leader[i].name,leader_name)==0)leader[i].count++;/*若是其中的一个候选人,则其票数加1*/
scanf(“%s”,leader_name);
}
for((i=0;i<3;i++)
printf(“%5s:
%d\n”,leader[i].name,leader[i].count);/*输出统计结果*/
}
11.2.4函数之间结构变量的数据传递
新的C语言标准允许相同数据类型的结构体变量可以整体赋值,所以,结构体变量可作为函数的实参,将数据整体传递给同类型的形参;也允许将结构体的成员作为函数的实参传递给同类型的形参。
下面对具体的传递形式分别给予介绍。
1.向函数传递结构体变量的成员
前面讲过,结构体的成员可以是简单变量、指针、数组等,虽然作了结构体的成员,但可以参加所属类型的所有操作。
因此,当简单变量、指针、数组名作函数的形参时,对应类型的结构体的成员可以作函数的实参。
用法和前面学习过的一样。
2.向函数传递结构体变量
新的C标准允许将结构体变量的值传递给对应的形参,系统将为结构体类型的形参开辟相应的存储单元,并将对应的实参各成员的值传递给对应的形参各成员。
结构体变量作函数的实参,与简单变量作函数的实参一样,也是单向的“值传递”。
例11.2有一个结构体变量stu,含有学生的学号、姓名和三门功课的成绩。
要求在main()函数中赋值,在print()函数中将值输出。
structstudent
{
intnum;
charname[20];
intscore[3];
};
voidprint(structstudents)
{inti;
printf(“%d,%s,”,s.num,s.name);
for(i=0;i<3;i++)
printf(“%d,”,s.score[I]);
}
main()
{
structstudentstu;
stu.num=12345;
strcpy(stu.name,”wanglin”);
stu.score[0]=98;
stu.score[1]=89;
stu.score[2]=76;
print(stu);
}
structstudent定义为外部数据类型,这样在本文件中的各函数都可以使用这种数据类型。
在print()函数中,形参的数据类型是structstudent结构体类型,主函数中,也是这种数据类型的实参stu,在调用print函数时,就将实参stu的值传递给对应的形参s,即将实参的各个成员的值传递给相应形参的对应成员。
因此,结构体变量作函数的参数与简单变量作函数的参数实质是一样的,也是单向的值传递。
3.向函数传递结构体变量的地址
向函数传递结构体类型数据的地址,对应的形参一定是指针或数组名。
要求实参的基类型与对应的形参的基类型要一致。
例11.3要求与例11.2相同,只是在print函数中,指针作函数的参数。
structstudent
{
intnum;
charname[20];
intscore[3];
};
voidprint(structstudent*p)
{inti;
printf(“%d,%s,”,p->num,p->name);
for(i=0;i<3;i++)
printf(“%d,”,p->score[I]);
}
main()
{
structstudentstu;
stu.num=12345;
strcpy(stu.name,”wanglin”);
stu.score[0]=98;
stu.score[1]=89;
stu.score[2]=76;
print(&stu);
}
比较一下,向函数传递结构体的地址与传递简单变量的地址有区别吗?
再举一个结构体类型的数组名作函数参数的例子。
例11.4有4个学生,每个学生包括学号、姓名、成绩,要求找出成绩最高的学生的姓名。
#include“stdio.h”
Structstudent
{
intnum;
charname[20];
intscore;
};
voidinput(structstudents[],intn);
{
intk;
for(k=0;k {scanf(“%d”,&s[k].num); gets(s[k].name); scanf(“%d”,&s[k].score); } } char*maxscore(structstudent*p,intn) { intk,high=0; for(k=1;k if(p[k].score returnp[high].name; } main() { structstudentst[10]; charname[20],*pname; pname=name; input(st,10); pname=maxscore(st,10); printf(“thehighscoreis: ”); printf(“%s”,pname); } 从上面可以看出,向函数传递结构体类型的地址,与前面学习过的,向函数传递简单变量的地址或数组名或者是指向数组的指针是一样的,也是将实参的地址值传递给对应的形参。 4.函数的返回值是结构体类型的值 例11.5 与例11.4相同,要求输出成绩最高的学生的信息。 我们只需改写maxscore函数,然后将这个函数的返回值在主函数中输出即可。 structstudentmaxscore(structstudent*p,intn) { intk,high=0; for(k=1;k if(p[k].score returnp[high]; } main() { structstudentst[10],s; input(st,10); s=maxscore(st,10); printf(“thehighscore: \n”); printf(“%d”,s.num); printf(“%s”,s.name); printf(“%d”,s.score); } 5.函数返回指向结构体的指针 函数返回指向结构体类型的指针,与前面讲过的函数返回基类型是基本数据类型的指针相同,只是指针的基类型不同。 例11.6 改写maxscore函数,使函数的返回值为指向结构体类型的指针。 structstudent*maxscore(structstudent*p,intn) { intk; structstudent*ph=p; for(k=1;k if(p[k].score returnph; } main() { structstudentst[10],*ps; input(st,10); ps=maxscore(st,10); printf(“thehighscore: \n”); printf(“%d”,ps->num); printf(“%s”,ps->name); printf(“%d”,ps->score); } 11.2.5链表 1.结构体中可以含有指向本结构体的指针成员 前面结构体的说明讲过,结构体的成员可以是指针,指针
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 语言 知识 11 章王厚财
![提示](https://static.bdocx.com/images/bang_tan.gif)