




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第7章结构体和共用体7.1概述7.2结构体类型定义与结构体变量的引用7.3结构体数组7.4结构体与函数7.5结构体与指针7.6链表7.7共用体7.8枚举类型7.9typedef的用法7.10结构体与共同体数据综合应用举例【教学目的与要求】
掌握结构体的定义、引用和结构体数组的定义、引用;认识使用指向结构体的指针处理链表建立动态数据结构的作用和意义;掌握指向结构体的指针和使用指向结构体的指针处理链表的方法;了解共同体。【教学重点与难点】
结构体的基本概念,结构体类型及变量的定义,结构体数组和用指针处理链表。
7.1概述问题定义:
为将不同数据类型、但相互关联的一组数据,组合成一个有机整体使用,C语言提供一种称为“结构体”的数据类型,它可以把多种类型的数据组合在一起,构成一个整体,一种新的数据类型,从而适应复杂类型数据的要求。如:一个学生:学号、姓名、性别、年龄、地址等属性
intnum;charname[20];charsex;intage;charaddr[30];应当把它们组织成一个组合项,在一个组合项中包含若干个类型不同(当然也可以相同)的数据项。
100101LiFunM18 87.5Beijing
Numnamesexagescoreaddr7.2.1结构体类型的定义
结构体是一种构造数据类型,它由若干成员构造而成。其成员可以是一个基本数据类型或者又是一个构造类型。结构体既是一种“构造”而成的数据类型,那么在说明和使用之前必须先定义它,也就是构造它。7.2结构体类型定义与结构体变量的引用用途:把不同类型的数据组合成一个整体--自定义数据类型struct
[结构体名]{
类型标识符成员名;类型标识符成员名;
…………….};成员类型可以是基本型或构造型struct是关键字,不能省略合法标识符可省:无名结构体结构体类型定义形式:成员名的命名应符合标识符的书写规定。
例如:
struct
student{charnum[10];charname[20];charsex;floatscore;};结构体名第一个成员为num,字符型数组;第二个成员为name,字符数组;第三个成员为sex,字符变量;第四个成员为score,实型变量。应注意在括号后的分号是不可少的。上述结构体类型定义之后,即可进行变量说明。凡被说明为结构体student类型的变量都由上述4个成员组成。
由此可见,结构体是一种构造的数据类型,是数目固定,类型不同的若干有序变量的集合。7.2.2结构体变量的定义与初始化structstudent{charnum[10];charname[20];
intage;};C语言中,结构体变量需要先使用结构体类型进行定义后才能使用。下面以学生类型的结构体类型为例说明如何定义结构体变量。学生类型的结构体定义如下:
在这里我们只关心学生这一类型的学号、姓名和年龄。当然作为学生还有很多特征我们可以根据程序的需要选择增加和减少相关特性。例structstudent{intnum;charname[20];charsex;
intage;floatscore;charaddr[30];};
structstudentstu1,stu2;结构体变量的三种定义方式struct
结构体名{
类型标识符成员名;类型标识符成员名;
…………….};struct
结构体名变量名表列;(1)先定义结构体类型,再定义结构体变量一般形式:在定义了结构体变量后,系统会为之分配内存单元。例如:stut1和stut2在内存中各占59个字节(2+20+1+2+4+30=59)(2)在声明类型的同时定义变量
struct
结构体名{
类型标识符成员名;类型标识符成员名;
…………….}变量名表列;例structstudent{intnum;charname[20];charsex;
intage;floatscore;charaddr[30];}stu1,stu2;struct{
类型标识符成员名;类型标识符成员名;
…………….}变量名表列;例struct
{intnum;charname[20];charsex;
intage;floatscore;charaddr[30];}stu1,stu2;用无名结构体直接定义变量一般形式:(3)直接定义结构体变量例structdate{intmonth;
intday;
intyear;};
structstudent{intnum;charname[20];
structdatebirthday;}stu;numnamebirthdaymonthdayyear说明结构体类型与结构体变量概念不同类型:不分配内存;变量:分配内存类型:不能赋值、存取、运算;变量:可以结构体可嵌套7.2.3结构体变量的引用
在ANSIC中除了允许具有相同类型的结构变量相互赋值以外,一般对结构变量的使用,包括赋值、输入、输出、运算等都是通过结构变量的成员来实现的。表示结构变量成员的一般形式是:
结构变量名.成员名其中“.”为成员(分量)运算符。(1)结构体变量引用的一般原则是:
不能将一个结构体变量作为整体进行输入和输出,而只能对结构体变量的各个成员进行输入输出。如:上例中可以有stu1.num=10010;
stu2.num=20010;(2)只能对结构体中最低级的成员进行赋值、存取以及运算。若成员本身又属于一个结构体类型,则必须要用若干成员运算符,一级一级地找到最低级的成员,才能进行操作。(3)对结构体变量的成员可以象普通变量一样进行各种运算(只要类型匹配)。(4)可以引用结构体变量成员的地址,也可以引用结构体变量的地址。例如:structstudentstu1,stu2;scanf(“%d”,&stu1.age);//输入age成员的值get(&stu1);//将结构体变量stu1的地址作为函数入
口参数结构体的初始化【例7-1】用初始化的方法给学生结构体变量赋值,并打印学生信息。structstudent//定义结构体类型{charnum[10];charname[20];intage;};structstudentstu[2]={{"20140101","张三",19},{"20140102","李四",18}},stu1={"20130101","王五",20},stu2={"20130102","陈六",21};//定义结构体变量定义初始化voidmain(){inti=0;for(i=0;i<2;i++)printf("学号:%s姓名:%s年龄:%d\n",stu[i].num,stu[i].name,stu[i].age);printf("学号:%s姓名:%s年龄:%d\n",stu1.num,,stu1.age);printf("学号:%s姓名:%s年龄:%d\n",stu2.num,,stu2.age);return0;}运行结果是:学号:20140101姓名:张三
年龄:19学号:20140102姓名:李四
年龄:18学号:20130101姓名:王五
年龄:20学号:20130102姓名:陈六
年龄:21【例7-2】用键盘输入的方法给学生结构体变量赋值,并打印学生信息.typedef
structstudent//定义结构体类型
{charnum[10];charname[20];
intage;}stud;intmain(){inti=0;studstu[2];//定义结构体变量数组
printf("学号
姓名
年龄\n");for(i=0;i<2;i++)
scanf("%s%s%d",stu[i].num,stu[i].name,&stu[i].age);for(i=0;i<2;i++)
printf("学号:%s姓名:%s年龄:%d\n”,stu[i].num,stu[i].name,
stu[i].age);
return0;}按如下数据输入:20140101张三1920140102李四20输出结果为:学号:20140101姓名:张三
年龄:19学号:20140102姓名:李四
年龄:20从以上两个例子,我们可以知道,结构体变量是一种数据组织形式,当我们通过成员操作符号“.”取出要使用的成员后,其使用方法与该成员的简单变量类型的使用方法完全一致,如stu[i].num
是字符串类型,我们按字符串的使用方法使用该成员,而stu[i].age是整形数,所以我们完全按照整形数变量来使用该成员。【例7-3】引用结构体内结构体的成员变量typedef
structdate//定义日期结构体{intyear;
intmonth;
intday;}date;typedef
structstudent//定义stud结构体类型{charnum[10];charname[20];
intage;datebirthday;}stud;int
main(int
argc,char*argv[]){inti=0; studstu;//定义结构体变量
printf("学号
姓名
年龄年月日\n");
scanf("%s%s%d%d%d%d",stu.num,,&stu.age, &stu.birthday.year, &stu.birthday.month, &stu.birthday.day);printf("学号:%s姓名:%s年龄:%d出生日期%d-%d-%d\n",stu.num,,stu.age,stu.birthday.year,stu.birthday.month,stu.birthday.day);return0;}从键盘输入:20140101张三21198256输出结果:学号:20140101姓名:张三
年龄:21出生日期1982-5-6例题中对于出身年月日的引用分别是:stu.birthday.year,stu.birthday.month,stu.birthday.day由于birthday是stu的一个成员所以我们引用birthday的方法是stu.birthday,由于birthday还是一个结构体变量所以我们对于它的三个成员继续用“.”引用【例7-4】结构体变量赋值操作#include<string.h>typedef
structdate//定义日期结构体{
intyear;
intmonth;
intday;}date;typedef
structstudent//定义stud结构体类型{charnum[10];charname[20];
intage;datebirthday;}stud;int
main(int
argc,char*argv[]){inti=0;studstu,stu1,stu2;//定义结构体变量
printf("学号
姓名
年龄年月日\n");scanf("%s%s%d%d%d%d",stu.num,,&stu.age,&stu.birthday.year,&stu.birthday.month,&stu.birthday.day);stu1=stu;//直接拷贝结构体变量strcpy(stu2.num,stu1.num);//逐项拷贝结构体变量strcpy(,);stu2.age=stu1.age;stu2.birthday.year=stu1.birthday.year;stu2.birthday.month=stu1.birthday.month;stu2.birthday.day=stu1.birthday.day;
printf("stu:学号:%s姓名:%s年龄:%d出生日期%d-%d-%d\n",
stu.num,,stu.age,stu.birthday.year,stu.birthday.month,
stu.birthday.day); printf("stu1:学号:%s姓名:%s年龄:%d出生日期%d-%d-%d\n",stu1.num,,stu1.age,stu1.birthday.year,stu1.birthday.month,stu1.birthday.day); printf("stu2:学号:%s姓名:%s年龄:%d出生日期%d-%d-%d\n",stu2.num,,stu2.age,stu2.birthday.year,stu2.birthday.month,stu2.birthday.day); return0;}从键盘输入:20140101张三21198256运行结果:stu:学号:20140101姓名:张三
年龄:21出生日期1982-5-6stu1:学号:20140101姓名:张三
年龄:21出生日期1982-5-6stu2:学号:20140101姓名:张三
年龄:21出生日期1982-5-6结构体变量有两种拷贝方法,第一种是直接使用“=”对结构体变量拷贝,如上例中stu1=stu;直接将stu的内容拷贝给stu1;第二种采用逐项赋值的方式拷贝结构体变量,这两种拷贝方法完全等效。如:strcpy(stu2.num,stu1.num);//逐项拷贝结构体变量strcpy(,);stu2.age=stu1.age;stu2.birthday.year=stu1.birthday.year;stu2.birthday.month=stu1.birthday.month;stu2.birthday.day=stu1.birthday.day;这两种拷贝方法完全等效。数组为相同类型的变量描述与使用带来了方便,如果数组元素是一个结构体变量,那么此数组被称为结构体变量,本节将介绍结构体变量及其应用。在使用过程中,对于相同类型的变量可以用数组进行描述,例如描述多个学生信息的变量,可以用数组进行描述。如果学生数组的每个元素是一个结构体变量,则该数组就可以表示多个学生的信息。这样的数组叫结构体数组。7.3结构体数组7.3.1结构体数组的定义与初始化1、结构体数组的定义结构体数组的定义只是结构体变量定义的特殊情况,因此定义的方法与结构体变量定义相同,有如下三种定义方法。(1)第一种形式,先定义结构体类型,然后再定义结构体类型的数组。例如:struct
stu_record
//定义一个结构体类型
{intnum;charname[10];……};struct
stu_recordstu[10]; //定义一个结构体数组,含10个
structstudent类型的元素(2)第二种定义方法,在定义类型的同时定义一个结构体类型的数组。例如:struct
stu_record{intnum;charname[20];……}stu[10]; //定义类型的同时定义一个数组(3)第三种形式,直接定义一个结构体类型的数组。例如:struct{intnum;charname[20];……}stu[10]; //直接定义一个结构体类型的数组2、结构体数组的初始化结构体类型数组的初始化遵循基本数据类型数组的初始化规律,在定义数组的同时,对其中的每一个元素进行初始化。例如:structstud{
intnum;charname[10];
intage;floatscore;}stu[2]={{10,“Zhangsan”,19,98.5},{13,“Ligao”,18,96.5,}};如果在定义数组的同时进行初始化,可以省略数组的长度,系统会根据初始化数据的多少来确定数组的长度。例如:struct
task_finished{chartask[20];chartag;}task_F_or_T[]={{“exer1”,“F”},{“exer2”,“T”},{“exer3”,“F”}};结构体数组task_F_or_T的长度,系统自动确认为3。注意:初始化是在定义的时候完成的,非定义时只有重新赋值,不能初始化!例如:structstudent{intnum;intage;floatscore};structstudentboy[2];boy[0]={102,19,96.5};boy[1]={103,18,95.5};……这时程序编译时会出错,原因是:结构体数组boy[2]的定义在语句structstudentboy[2]时已完成,因此boy[0]={102,19,96.5}和语句boy[1]={103,18,95.5}就不能看成是初始化,它是重新赋值,C语法是禁止结构体这样赋值的!这时程序编译时会出错,原因是:结构体数组boy[2]的定义在语句structstudentboy[2]时已完成,因此boy[0]={102,19,96.5}和语句boy[1]={103,18,95.5}就不能看成是初始化,它是重新赋值,C语法是禁止结构体这样赋值的!7.3.2结构体变量的使用(1)结构体变量的成员使用方式
结构体变量名.成员名
如:stu[i].nmu、stu[i].name、stu[i].agestu1.nmu、、stu1.agestu2.nmu、、stu2.age结构体变量的成员按照它的简单变量类型的使用方法直接使用。【例7-5】用键盘输入的方法给学生结构体变量赋值,并打印学生信息.typedef
structstudent//定义结构体类型
{charnum[10];charname[20];intage;}stud;voidmain(){inti=0;studstu[2];//定义结构体变量数组printf("学号
姓名
年龄\n");for(i=0;i<2;i++)
scanf("%s%s%d",stu[i].num,stu[i].name,&stu[i].age);}for(i=0;i<2;i++)
printf("学号:%s姓名:%s年龄:%d\n",
stu[i].num,stu[i].name,stu[i].age);return0;}按如下数据输入:20140101张三1920140102李四20输出结果:学号:20140101姓名:张三
年龄:19学号:20140102姓名:李四
年龄:20
从以上例子,可以知道,结构体变量是一种数据组织形式,当我们通过成员操作符号“.”取出要使用的成员后,其使用方法与该成员的简单变量类型的使用方法完全一致.
如stu[i].num
是字符串类型,我们按字符串的使用方法使用该成员,而stu[i].age是整形数,所以完全按照整形数变量来使用该成员。当结构体内部的成员是一个结构体的情况:【例7-6】引用结构体内结构体的成员变量示例。typedef
structdate//定义日期结构体{intyear;intmonth;intday;}date;typedef
structstudent//定义stud结构体类型{charnum[10];charname[20];
intage;datebirthday;}stud;intmain(){inti=0;studstu;//定义结构体变量
printf("学号
姓名
年龄年月日\n");
scanf("%s%s%d%d%d%d",stu.num,,&stu.age,&stu.birthday.year,&stu.birthday.month,&stu.birthday.day);printf("学号:%s姓名:%s年龄:%d出生日期%d-%d-%d\n",stu.num,,stu.age,stu.birthday.year,stu.birthday.month,stu.birthday.day);return0;} 例题中对于出身年月日的引用分别是:stu.birthday.year,stu.birthday.month,stu.birthday.day由于birthday是stu的一个成员所以我们引用birthday的方法是stu.birthday,由于birthday还是一个结构体变量所以我们对于它的三个成员继续用“.”引用,分别是:出生年份stu.birthday.year;出生月份stu.birthday.month;出生日
stu.birthday.day;【例7-7】结构体变量赋值操作#include"string"typedef
structdate//定义日期结构体{intyear;intmonth;intday;}date;typedef
structstudent//定义stud结构体类型{charnum[10];charname[20];intage;datebirthday;}stud;intmain(){inti=0;studstu,stu1,stu2;//定义结构体变量printf("学号
姓名
年龄年月日\n");scanf("%s%s%d%d%d%d",stu.num,,&stu.age,&stu.birthday.year,&stu.birthday.month,&stu.birthday.day);stu1=stu;//直接拷贝结构体变量strcpy(stu2.num,stu1.num);//逐项拷贝结构体变量strcpy(,);stu2.age=stu1.age;stu2.birthday.year=stu1.birthday.year;stu2.birthday.month=stu1.birthday.month;stu2.birthday.day=stu1.birthday.day;printf("stu:学号:%s姓名:%s年龄:%d出生日期%d-%d-%d\n",stu.num,,stu.age,stu.birthday.year,
stu.birthday.month,
stu.birthday.day); printf("stu1:学号:%s姓名:%s年龄:%d出生日期%d-%d-%d\n",stu1.num,,stu1.age,stu1.birthday.year,stu1.birthday.month,stu1.birthday.day); printf("stu2:学号:%s姓名:%s年龄:%d出生日期%d-%d-%d\n",stu2.num,,stu2.age,stu2.birthday.year,stu2.birthday.month,stu2.birthday.day); return0;}从键盘输入:20140101张三21198256运行结构:stu:学号:20140101姓名:张三
年龄:21出生日期1982-5-6stu1:学号:20140101姓名:张三
年龄:21出生日期1982-5-6stu2:学号:20140101姓名:张三
年龄:21出生日期1982-5-6结构体变量有两种拷贝方法,第一种是直接使用“=”对结构体变量拷贝,如上例中stu1=stu;直接将stu的内容拷贝给stu1;第二种采用逐项赋值的方式拷贝结构体变量,如:strcpy(stu2.num,stu1.num);//逐项拷贝结构体变量strcpy(,);stu2.age=stu1.age;stu2.birthday.year=stu1.birthday.year;stu2.birthday.month=stu1.birthday.month;stu2.birthday.day=stu1.birthday.day;这两种拷贝方法完全等效。【例7-8】建立同学通讯录。#include"stdio.h"#defineN3structyearbook{charname[20];charphone[10];};main(){structyearbookcla[N];
inti;for(i=0;i<N;i++)
{printf("inputname:\n");
gets(cla[i].name);
printf("inputphone:\n");
gets(cla[i].phone);}
printf("name\t\t\tphone\n\n");
for(i=0;i<NUM;i++)printf("%s\t\t\t%s\n",cla[i].name,cla[i].phone);}本程序中定义了一个结构体yearbook,它有两个成员name和phone用来表示姓名和电话号码。在主函数中定义cla为具有yeaebook类型的结构数组。在for语句中,用gets函数分别输入各个元素中两个成员的值。然后又在for语句中用printf语句输出各元素中两个成员值。7.4结构体与函数7.4.1结构体变量作函数的参数结构体变量作为函数的形式参数时和简单变量作为形式参数一样,函数在调用时会为结构体变量在栈中分配该结构体变量相同大小的空间,并将结构体变量实际参数的内容拷贝到该空间里。函数结束后该空间被释放。函数中对结构体变量做的任何修改将,函数退出后不再有效。【例7-9】结构体变量作为函数的形式参数。typedef
structdate//定义日期结构体{intyear;
intmonth;
intday;}date;typedef
structstudent//定义stud结构体类型{charnum[10];charname[20];
intage;datebirthday;}stud;voidprintfstruct(stud
stu)//结构体变量作为函数参数{stu.birthday.year=0;stu.birthday.month=0;stu.birthday.day=0; printf("学号:%s姓名:%s年龄:%d出生日期%d-%d-%d\n",stu.num,,stu.age,stu.birthday.year,stu.birthday.month,
stu.birthday.day); //函数内部打印结构体形式参数内容}intmain(){
inti=0;studstu;//定义结构体变量
printf("学号
姓名
年龄年月日\n");
scanf("%s%s%d%d%d%d",stu.num,,&stu.age,&stu.birthday.year,&stu.birthday.month, &stu.birthday.day);
printfstruct(stu);
printf("学号:%s姓名:%s年龄:%d出生日期%d-%d-%d\n",stu.num,,stu.age,stu.birthday.year,stu.birthday.month,stu.birthday.day); //调用函数后再次打印结构体内容return0;}从键盘输入:20140101张三21198256运行结果:学号:20140101姓名:张三
年龄:21出生日期0-0-0学号:20140101姓名:张三
年龄:21出生日期1982-5-6由于形式参数stu被定义为结构体,在函数内部依然使用“.”引用形式参数的各成员。和简单变量一样在函数内部使用的是实际参数的副本,如果函数中对结构体内部成员的值做了改变,当函数结束时,这些改变将不会在实际参数上生效。输出结构第一次打印时是在函数内部,将出生年月日都改为了0,显示的结果是出生年月日都为0,当函数退出再次打印出生年月日时,还是保持原来的值。当我们想通过函数改变结构体变量的值时,必须采用指针作为函数的参数才能真正改变结构体变量的值。由于在函数内部是使用指针操作结构体变量,直接操作的是结构体实际存储的空间,所以这样的修改是生效的。7.4.2返回值为结构体变量的函数在一个函数的内部,函数的返回值通过return语句实现。return返回的通常是一个拷贝,不管是变量、对象还是指针都是返回拷贝。返回值分为以下几种情况:1)返回一个基本类型的变量;2)返回非动态分配的指针;3)返回动态分配的指针;4)返回非基本类型(对象等)。本节将以例子的形式介绍返回值为结构体的函数。【例7-10】函数的返回值为结构体变量。#include<stdio.h>structdate{int
year,month,day;};structdateinput(){structdatet;
scanf("%d%d%d",&t.year,&t.month,&t.day);returnt;}voiddisp(structdated){printf("%d/%d/%d\n",d.year,d.month,d.day);}intmain(){structdated1;d1=input();disp(d1);}程序运行时从键盘输入:201111程序运行后的输出结果是:2011/1/1通过以上例子可以看出,函数返回变量类型为结构体类型时对解决复杂问题带来方便,可以通过简洁的描述应对复杂的数据要求。7.5结构体与指针7.5.1结构体指针的定义与使用如果指针变量中存放某个结构体变量的首地址时,则此指针变量被称为结构指针变量。此时可以通过结构指针访问该结构变量,这与数组指针和函数指针的情况是相同的。结构指针变量通常需要先说明后使用,其说明的一般形式为:struct
结构名*结构指针变量名例如,在前面的例题中定义date这个结构,如要说明一个指向date的指针变量pdat,可写为:structdate*pdat;当然也可在定义date结构时同时说明pdat。与前面讨论的各类指针变量相同,结构指针变量也必须要先赋值后才能使用。
赋值是把结构变量的首地址赋予该指针变量,不能把结构名赋予该指针变量。如果birthday是被说明为date类型的结构变量,则:pdat=&birthday是正确的,而:pdat=&date是错误的。结构名和结构变量是两个不同的概念,不能混淆。结构名只能表示一个结构形式,编译系统并不对它分配内存空间。只有当某变量被说明为这种类型的结构时,才对该变量分配存储空间。因此上面&date这种写法是错误的,不可能去取一个结构体类型名的首地址。有了结构指针变量,就能更方便地访问结构体变量的各个成员。其访问的一般形式为:(*结构体指针变量).成员名或为:
结构体指针变量->成员名例如:(*pdat).year或者:pdat->year应该注意(*pdat)两侧的括号不可少,因为成员符“.”的优先级高于“*”。如去掉括号写作*pdat.year则等效于*(pdat.year),这样,意义就完全不对了。
下面的例题将为我们说明如何使用指向结构体的指针变量引用结构体。【例7-13】通过指针引用结构体变量。typedef
structstudent//定义结构体类型
{charnum[10];charname[20];
intage;}stud;int
main(int
argc,char*argv[]){inti=0;stud*pstu=NULL;//定义结构体指针变量studstu;//定义结构体变量
pstu=&stu;//结构体指针变量指向结构体变量
printf("学号
姓名
年龄\n");scanf("%s%s%d",pstu->num,pstu->name,&pstu->age);printf("学号:%s姓名:%s年龄:%d\n",pstu->num,pstu->name,pstu->age);return0;}从键盘输入:20140101张三19运行结果:学号:20140101姓名:张三
年龄:19此例题说明,和简单变量指针变量一样,结构体指针变量使用前需要给结构体指针变量赋值,该值等于结构体变量的地址。如:
pstu=&stu;//结构体指针变量指向结构体变量将stu结构体变量的地址&stu地址赋值给pstu;当使用结构体指针引用结构体变量成员时不再使用“.”成员符号,而是使用“->”引用成员变量,如:pstu->num、pstu->name、pstu->age。7.5.2结构体指针与结构体数组指针变量可以指向一个结构体数组,这时结构体指针变量的值是整个结构体数组的首地址。结构体指针变量也可指向结构体数组的一个元素,这时结构体指针变量的值是该结构体数组元素的首地址。设ps为指向结构体数组的指针变量,则ps也指向该结构体数组的0号元素,ps+1指向1号元素,ps+i则指向i号元素。这与普通数组的情况是一致的。【例7-14】用指针变量输出结构体数组。struct
stu{
intnum;char*name;charsex;floatscore;}boy[5]={{101,"Zhouping",'M',45},{102,"Zhangping",'M',62.5},{103,"Lioufang",'F',92.5},{104,"Chengling",'F',87},{105,"Wangming",'M',58}};main(){
struct
stu*ps;
printf("No\tName\t\t\tSex\tScore\t\n");
for(ps=boy;ps<boy+5;ps++)printf("%d\t%s\t\t%c\t%f\t\n",ps->num,ps->name,ps->sex,ps->score);}在程序中,定义了stu结构体类型的外部数组boy并作了初始化赋值。在main函数内定义ps为指向stu类型的指针。在循环语句for的表达式1中,ps被赋予boy的首地址,然后循环5次,输出boy数组中各成员值。应该注意的是,一个结构体指针变量虽然可以用来访问结构体变量或结构体数组元素的成员,但是,不能使它指向一个成员。也就是说不允许取一个成员的地址来赋予它。因此,下面的赋值是错误的。ps=&boy[1].sex;而只能是:ps=boy;(赋予数组首地址)或者是:ps=&boy[0];(赋予0号元素首地址)在ANSIC标准中允许用结构体变量作函数参数进行整体传送。但是这种传送要将全部成员逐个传送,特别是成员为数组时将会使传送的时间和空间开销很大,严重地降低了程序的效率。因此最好的办法就是使用指针,即用指针变量作函数参数进行传送。这时由实参传向形参的只是地址,从而减少了时间和空间的开销。【例7-15】计算一组学生的平均成绩和不及格人数。用结构体指针变量作函数参数编程。struct
stu{
intnum;char*name;charsex;floatscore;}boy[5]={{101,"Liping",'M',45},{102,"Zhangping",'M',62.5},{103,"Hefang",'F',92.5},{104,"Chengling",'F',87},{105,"Wangming",'M',58},};main(){struct
stu*ps;voidave(struct
stu*ps);
ps=boy;
ave(ps);}voidave(struct
stu*ps){intc=0,i;floatave,s=0;
for(i=0;i<5;i++,ps++)
{s+=ps->score;
if(ps->score<60)c+=1;}
printf("s=%f\n",s);
ave=s/5;
printf("average=%f\ncount=%d\n",ave,c);}本程序中定义了函数ave,其形参为结构体指针变量ps。boy被定义为外部结构体数组,因此在整个源程序中有效。在main函数中定义说明了结构体指针变量ps,并把boy的首地址赋予它,使ps指向boy数组。然后以ps作实参调用函数ave。在函数ave中完成计算平均成绩和统计不及格人数的工作并输出结果。由于本程序全部采用指针变量作运算和处理,故速度更快,程序效率更高。C语言中使用数组时,数组的长度由定义说明,整个程序中固定不变。C语言中不允许动态数组类型。但在实际应用中,所需的内存空间取决于实际输入的数据,而无法预先确定。显然用数组的办法很难解决。为了解决上述问题,C语言提供了一些内存管理函数,这些内存管理函数可以按需要动态地分配内存空间,也可把不再使用的空间回收待用。此时引入链表,本节将介绍链表的定义及其应用。7.6链表7.6.1链表的定义相同类型的结点占有同样大小的内存空间,在使用过程中,为了解决结点数目动态变化问题,链表是一种有效的解决方法。例如使用动态存储的方法解决学生数目变化的情况。有一个学生就分配一个结点,无须预先确定学生的准确人数,某学生退学,可删去该结点,并释放该结点占用的存储空间。从而节约了宝贵的内存资源。另一方面,用数组的方法必须占用一块连续的内存区域。而使用动态分配时,每个结点之间可以是不连续的(结点内是连续的)。结点之间的联系可以用指针实现。即在结点结构中定义一个成员项用来存放下一结点的首地址,这个用于存放地址的成员,常把它称为指针域。可在第一个结点的指针域内存入第二个结点的首地址,在第二个结点的指针域内又存放第三个结点的首地址,如此串连下去直到最后一个结点。最后一个结点因无后续结点连接,其指针域可赋为0。这样一种连接方式,在数据结构中称为“链表”。链表的简单示意如图7-1:图7-1链表示意图图7-1中,第0个结点称为头结点,它存放有第一个结点的首地址,它没有数据,只是一个指针变量。以下的每个结点都分为两个域,一个是数据域,存放各种实际的数据,如学号num,姓名name,性别sex和成绩score等。另一个域为指针域,存放下一结点的首地址。链表中的每一个结点都是同一种结构类型。例如,一个存放学生学号和成绩的结点应为以下结构:struct
stu{intnum;intscore;struct
stu*next;}前两个成员项组成数据域,后一个成员项next构成指针域,它是一个指向stu类型结构的指针变量。7.6.2链表的常用操作链表的基本操作对链表的主要操作有以下几种:建立链表;结构的查找与输出;插入一个结点;删除一个结点;下面通过例题来说明这些操作。【例7-16】建立一个三个结点的链表,存放学生数据。为简单起见,我们假定学生数据结构中只有学号和年龄两项。可编写一个建立链表的函数creat。#defineNULL0#defineTYPEstruct
stu#defineLENsizeof(struct
stu)struct
stu{intnum;intage;struct
stu*next;};TYPE*creat(intn){struct
stu*head,*pf,*pb;inti;for(i=0;i<n;i++){
pb=(TYPE*)malloc(LEN);
printf("inputNumberandAge\n");
scanf("%d%d",&pb->num,&pb->age);
if(i==0)
pf=head=pb;elsepf->next=pb;
pb->next=NULL;
pf=pb;}
return(head);}在函数外首先用宏定义对三个符号常量作了定义。这里用TYPE表示struct
stu,用LEN表示sizeof(struct
stu)主要的目的是为了在以下程序内减少书写并使阅读更加方便。结构stu定义为外部类型,程序中的各个函数均可使用该定义。creat函数用于建立一个有n个结点的链表,它是一个指针函数,它返回的指针指向stu结构。在creat函数内定义了三个stu结构的指针变量。head为头指针,pf为指向两相邻结点的前一结点的指针变量。pb为后一结点的指针变量。【例7-17】链表的建立与内容输出。#include<stdio.h>structdate{
int
year,month,day;
structdate*next;};intmain(){structdated1={2010,10,10,0};
structdated2={2010,12,31,0};
structdated3={2011,1,1,0};
structdate*p=&d1;d1.next=&d2;d2.next=&d3;d3.next=NULL;while(p!=NULL){printf("%d/%d/%d\n",p->year,p->month,p->day);p=p->next;}}程序的运行结果是:2010/10/102010/12/312011/1/1结构体数据类型由用户定义的多个成员组成,各个成员占用不同的内存空间。本节将介绍共用体构造数据类型。7.7共用体7.7.1共用体类型的定义结构体由不同的数据成员组成,并且各成员各自占有内存空间。共用体也是一种由用户自定义的数据类型,也可以由若干种数据类型组合而成,组成共用体数据的若干个数据也称为成员。和结构体不同的是,共用体是把几种不同类型的变量放在同一段内存单元中,且各个变量都是从同一地址开始存放。即使用覆盖技术,几个变量相互覆盖,从而达到使几个不同的变量共同占用同一段内存的目的。假定在一个C语言程序中定义了一个共用体变量S,S中含有三个不同类型的成员,第一成员为字符型变量ch需要一个存储单元,第二个成员为整型变量i需要2个存储单元,第三个成员为单精度型变量f需要四个存储单元。实际上定义共用体变量S时,系统只给变量S分配4个存储单元,3个成员间的对应关系为:共用体变量定义时占用的存储单元是其成员所占用的最大存储单元数,各成员不能同时使用存储单元,每次均只能一个成员使用共用体存储单元。图7-2共用体变量S占用的存储单元数为4。定义共用体类型的方法与定义结构体类型的方法类似:union共用体名{数据类型1成员名1;数据类型2成员名2;……数据类型n成员名n;}共用体变量列表;其中:共用体名是用户命名的标识符。数据类型通常是基本数据类型,也可以是结构体类型、共用体类型等其它类型。成员名是用户取的标识符,用来标识所包含的成员名称。例如:unionexample{chara[100];//该成员占用100个存储单元intb[100];//该成员占用400个存储单元floatc[100];//该成员占用400个存储单元}array;//共用体变量array占用400个存储单元注意:共用体变量中每个成员所占的存储单元是连续的,而且都是从分配的连续存储单元的第一个存储单元开始存放数据,所以对共用体变量来说所有成员的首地址是相同的。7.7.2共用体变量的定义与引用1、共用体变量的定义定义了某个共用体后,就可以使用它来定义相应共用体类型的变量、数组等。方法如下:(1)先定义共用体,然后定义变量、数组。(2)同时定义共用体和变量、数组。(3)定义无名称的共用体的同时定义变量、数组。(1)unionexam{inti;charch;};unionexama,m[3];(2)unionexam{inti;charch;}a,m[3];(3)union{
inti;charch;}a,m[3];由于共用体变量中的成员不能同时起作用,因此对共用体变量、数组的定义不能赋初值,只能在程序中对其成员进行赋值。2、共用体变量的引用与结构体变量的引用方式类似:先定义,后使用;只能引用具体成员,而不能直接引用变量。例如,前面已定义了共用体变量a,下面的引用方式是正确的:a.i(引用共用体变量中的整型变量i)a.ch(引用共用体变量中的字符型变量ch)不能只引用共用体变量,如:printf(“%d”,a);是错误的。这是因为a的存储区有2种类型,分别占不同长度的存储空间,仅写变量名a,难以让系统确定究竟是要输出哪一个成员。应该写成printf(“%d”,a.i)或printf(“%c”,a.ch)。【例7-18】写出下列程序的输出结果。main(){unionzj{inta;charch[2];}au;au.a=298;printf("%d,%d\n",au.ch[0],au.ch[1]);}运行结果为:42,17.8枚举类型实际应用中,有些变量的取值被限制在特定的范围内。例如,一年只有特定的十二个月,一个星期只有特定的七天等等。对于此类变量,C语言提供了一种称为“枚举”的类型。在“枚举”类型的定义中列举出所有可能的取值,被说明为该“枚举”类型的变量取值不能超过定义的范围。但要说明的是,枚举类型是一种基本数据类型,而不是一种构造类型,因为它不能再分解为任何基本类型。1、枚举类型的定义与枚举变量说明1)枚举类型定义的一般形式为:enum
枚举名{枚举值表};在枚举值表中应罗列出所有可用值。这些值也称为枚举元素。例如:enumweekday{monday,,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday};该枚举名为weekday,枚举值共有7个,即一周中的七天。凡被说明为weekday类型变量的取值只能是七天中的某一天。(2)枚举变量的说明如同结构体和联合体一样,枚举变量也可用不同的方式说明,即先定义后说明,同时定义说明或直接说明。设有变量a,b,c被说明为上述的weekday,可采用下述任一种方式:变量说明方式一:enumweekday{sun,mou,tue,wed,thu,fri,sat};enumweekdaya,b,c;变量说明方式二:enumweekday{sun,mou,tue,wed,thu,fri,sat}a,b,c;变量说明方式三:enum{sun,mou,tue,wed,thu,fri,sat}a,b,c;2、枚举类型的变量赋值和使用枚举类型在使用中有以下规定:枚举值是常量,不是变量。不能在程序中用赋值语句再对它赋值。例如对枚举weekday的元素再作以下赋值:sun=5;mon=2;sun=mon;都是错误的。枚举元素本身由系统定义了一个表示序号的数值,从0开始顺序定义为0,1,2…。如在weekday中,sun值为0,mon值为1,…,sat值为6。【例7-19】枚举类型赋值。main(){enumweekday{sun,mon,tue,wed,thu,fri,sat}a,b,c;
a=sun;b=mon;c=tue;
printf("%d,%d,%d",a,b,c);}说明:只能把枚举值赋予枚举变量,不能把元素的数值直接赋予枚举变量。如:a=sum;b=mon;是正确的。而:a=0;b=1;是错误的。如一定要把数值赋予枚举变量,则必须用强制类型转换。如:a=(enumweekday)2;其意义是将顺序号为2的枚举元素赋予枚举变量a,相当于:a=tue。还应该说明的是枚举元素不是字符常量也不是字符串常量,使用时不要加单、双引号。【例7-20】枚举类型的使用。main(){enumbody{a,b,c,d}month[31],j;inti;j=a;for(i=1;i<=30;i++){
month[i]=j;j++;if(j>d)j=a;}
for(i=1;i<=30;i++){
switch(month[i]){casea:printf("%2d%c\t",i,'a');break;caseb:printf("%2d%c\t",i,'b');break;casec:printf("%2d%c\t",i,'c');break;cased:printf("%2d%c\t",i,'d');break;
default:break;}}
printf("\n");}7.9.1用typedef说明一种新类型
在C语言程序中,除了可以直接使用C提供的标准类型名(int,char,float,double,long等),和自己声明的结构体,共用体,指针,枚举类型外,还可以用typedef
声明新的类型名来代替已有的类型名。功能:用自定义名字为已有数据类型命名简单形式:
typedef
typename
newname;类型定义语句关键字已有数据类型名用户定义的类型名7.9typedef的用法说明:1.typedef没有创造新数据类型2.typedef
是定义类型,不能定义变量3.typedef与define不同
definetypedef预编译时处理编译时处理简单字符置换为
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2024年农艺师考试重要知识点归纳试题及答案
- 2024年微生物检验技师考试难究试题及答案
- 2025年注会统计数据分析试题及答案
- 2024年项目管理专业人士资格认证考试题型分析试题及答案
- 2024年项目管理资格的考前须知试题及答案
- 股票分析与投资判断技巧的考题及答案
- 2025年证券从业资格证凭证试题及答案
- 室内地下吊装口施工方案
- 2024年项目管理专业知识扩展试题及答案
- 皮肤养生保健与天然护肤法考核试卷
- 家长会课件:七年级家长会班主任优质课件
- 人工智能导论智慧树知到课后章节答案2023年下哈尔滨工程大学
- 脑中风病人病情观察
- 第14课 背影 课件(共26张ppt)
- 汽车维修工(三级)技能理论考试题库(浓缩300题)
- 石景山区行政事业单位资产清查业务培训
- 《今天怎样做教师-点评100个教育案例》读书分享会PPT模板
- 高效节水灌溉技术与灌溉排水工程设计及案例分析
- 《将军胡同》阅读试题及答案
- 2022年常德市汉寿县社区工作者招聘考试试题
- 福建土楼介绍
评论
0/150
提交评论