第十章结构体与共用体10.1结构体类型与结构体变量的定义10.2结构变量的初始化与引用10.3结构体数组10.4指向结构体类型数据的指针10.5结构体和函数10.6单链表10.7共用体10.8枚举类型10.9定义已有类型的别名教学提示前6章介绍了五种基本数据类型和指针类型,还介绍了一种构造类型——数组,它是用基本数据类型构造出来的具有相同类型变量的集合。当要处理那些具有不同类型、而又相互关联的一组数据时,数组就显得力不从心。本章将介绍另外几种构造类型——结构体、共用体和枚举。它们可以是不同类型变量的集合,其中最重要的是结构体类型,它可以方便地处理像“记录”、“链表”这样的静态和动态数据结构。教学目标掌握结构体类型、结构体变量、结构体数组、结构体指针的定义和引用方法,掌握结构体变量、结构体数组和结构体指针变量在函数间的传递规则,能够用结构体进行链表的简单操作,了解共用体及枚举类型的概念、定义和引用,学会已有类型的别名定义方法。概述在一些复杂的数据结构中,有时需要将不同类型的数据集合成一个有机的整体。如:一个学生的情况纪录单可能包括学号、姓名、性别、年龄、成绩、家庭地址等数据项。这样的整体,C语言中称为“结构体”数据结构,简称“结构体”(structure)。结构体是一种较为复杂而又非常灵活的构造型的数据类型。一个结构体类型的数据可以由若干个称为成员(或域)的成分组成。不同的结构体类型其成员不同。对于一个具体的结构体而言,其成员的数量是固定的,这一点与数组相同,但该结构体中各成员的数据类型可以不同,这是结构体与数组的重要区别。10.1结构体类型与结构体变量的定义10.1.1结构体类型定义结构体类型定义的一般形式:struct结构体名{数据类型数据项1;数据类型数据项2;…………数据类型数据项n;};其中struct是关键字,结构体成员表列也称域表,每个成员也称结构体中的一个域。对每个成员都应进行类型说明。例如:structstudent{intnum;charname[20];charsex;intage;floatscore;};对结构体类型定义的说明1结构体类型定义是由程序员根据设计需要自行定义的,因此结构体类型可以有多种,每种结构体类型都可以有自己的结构体名以及包含不同数目的成员。2若定义了一个结构体类型,那仅仅是定义类型而已,而不分配内存单元。例如上面已经定义了的structstudent结构体类型,structstudent可以用来定义一个该类型的变量,并不意味着它的那些成员被分配了内存空间。3成员名可以与程序中的变量名相同,两者不代表同一对象。例如,程序中可以另定义变量num,它与structstudent中的num是两回事,互不干扰。4结构体成员类型可以是整型、实型、字符型、数组、指针等基本类型或构造类型,还可以是已定义过的结构体类型。structdate{intyear;intmonth;intday;};structstudent{intnum;charname[20];charsex;structdatebirthday;floatscore[4];};练习:定义一个学生成绩结构类型:由学号和三门成绩共4项组成。10.1.2结构体变量定义结构体类型的定义只是指出了该结构的组成情况,表明存在有此种类型的结构模型。该结构体类型中不能存放具体的数据,系统也不会为它分配实际的存贮单元。为了能在程序中使用结构体类型的数据,应在定义了某种结构体类型以后,再定义该结构体类型的变量,以便在结构体类型的变量中存放具体的数据。结构体变量的定义有三种形式:结构体变量定义11.先声明结构体类型,再定义结构体类型的变量。structstudent{intnum;charname[20];charsex;intage;floatscore[4];};structstudentst1,st2;numnamesexagescore结构体变量定义22.在声明结构体类型的同时定义结构体类型的变量。structstudent{intnum;charname[20];charsex;intage;floatscore[4];}st1,st2;结构体变量定义33.直接定义结构体类型变量。struct{intnum;charname[20];charsex;intage;floatscore[4];}st1,st2;在关键字struct后省略了结构体名。不提倡使用这种形式。可用sizeof来计算一个结构体类型数据的长度如:sizeof(structstudent)或sizeof(st1)有关结构体变量的说明(1)结构体类型与结构体变量是两个不同的概念,其区别如同int类型与int型变量的区别一样。(2)结构体类型中的成员名,可以与程序中的变量同名,它们代表不同的对象,互不干扰。(3)定义了结构体变量后,系统会为之分配内存单元。如上例中的st1,st2在内存中各占37个字节。可用sizeof来计算一个结构体类型数据的长度。如:sizeof(structstudent)或sizeof(st1)。练习2定义一个学生信息结构类型:由学号、姓名、性别和生日共4项组成,其中生日由日期结构类型构成。10.2结构变量的初始化与引用初始化就是在定义变量的同时给变量赋初值,例如:structstudent{intnum;charname[8],sex;structdate{intyear,month,day;}birthday;floatscore[4];}st1={101,“Xu,‘F',1975,9,12,83.5,88,75.5,90};•在对结构体变量进行初始化时,系统是按每个成员在结构体中的顺序一一对应赋初值的。若只对部分成员进行初始化,则只能给前面的若干成员赋值,而不允许跳过前面的成员给后面的成员赋值。未赋值部分均为0值。结构体变量的引用1.结构体变量整体引用如果要将结构体变量整体引用则往往只限于将一个结构体变量直接赋值给另一个具有相同类型的结构体变量。structstudent{intnum;charname[8],sex;structdate{intyear,month,day;}birthday;floatscore[4];}st1={101,,Xu,'F',1975,9,12,83.5,88,75.5,90};structstudentst2;st2=st1;程序执行后变量st2中各成员的值都完全与st1各成员的值相等。2.结构体变量成员引用对结构体成员的引用方式为:结构体变量名.成员名其中,“.”为结构体成员运算符,它的优先级处于所有运算符优先级的最高级别。例如:st1.num表示st1变量中的num成员,可以对它赋值st1.num=1001,这时st1.num就相当于一个整型数。结构体变量引用规则说明1结构体变量整体不能直接用来输入输出。如:scanf(%d,%s,%c,%d,%f,&st1);但可逐个全部或部分输入输出。如:scanf(“%d%c%f”,&st1.num,&st1.sex,&st1.score[2]);2若成员本身又是一个结构体类型,则必须逐层使用成员名定位,找到最底层的成员。例如在结构体变量st1中对成员year的引用方式为:st1.birthday.year。3若结构体中的成员是字符型数组时,则可将其看作是“字符串变量”,而直接引用。例如:对st1中name的引用可写成:st1.name。4若结构体中的成员是数值型数组时,则对该数组成员的引用,应该为对该数组元素的引用。例如:对st1中score数组元素的引用可写成:st1.score[0],st1.score[1],st1.score[2],st1.score[3]。Wrong!我们可以将结构体中变量的每个成员当作是同类型的普通变量,对它进行同类变量所允许的任何操作。例如成员变量st1.name是字符串,可以对它进行字符串变量所允许的任何操作,包括输入、输出。同样对于成员中的数组元素也可按同类型的数组元素进行操作。如:scanf(%s,st1.name);/*对结构体成员name赋值*/for(i=0;i4;i++)scanf(%f,&st1.score[i]);/*对结构体数组成员score赋值*/st1.num++;/*结构体成员num自增运算*/st1.sex=getchar();/*对结构体成员sex进行赋值*/st1.brithday.year=1975;floatf;for(i=0;i4;i++)/*在TC中的赋值语句*/{scanf(%f,&f);st1.score[i]=f;}例写程序给结构体变量赋初值。structstudent{intnum;charname[20],sex;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);}10.3结构体数组结构体数组是同类型结构体变量的集合。1.结构体数组的定义和初始化structstudent{intnum;charname[20];charsex;intage;floatscore;}sta[3]={{1001,LiLi,'m',20,75.0},{1002,“fangfang,'m',21,86.0},{1003,“yuanyuan,'f',19,91.0}};2.结构体数组的输入和输出对结构体数组数据的输入或输出可利用for循环结构,例如inti;for(i=0;i2;i++)scanf(%d%s%c%d%f,&sta[i].num,sta[i].name,&sta[i].sex,&sta[i].age,&sta[i].score);这是给数组sta[3]的元素赋值操作。例1:求3个学生的平均成绩。main(){structstudent{intnum;charname[20];charsex;intage;floatscore;}st[3];inti;floataverage=0.;for(i=0;i3;i++){printf(\nnum=);scanf(%d%,&st[i].num);getchar();printf(name=);gets(st[i].name);printf(sex=);st[i].sex=getchar();printf(age=);scanf(%d,&st[i].age);printf(score=);scanf(%d,&st[i].score);average+=st[i].score;}average/=3.;printf(\nNonamesexscoreaddr);for(i=0;i3;i++)printf(\n%4d%10s%2c%3d,%10.2f,st[i].num,st[i].name,st[i].sex,st[i].age,st[i].score);printf(\ntheaverage=%10.2f\n,average);}10.4指向结构体类型数据的指针1.定义和初始化定义指向结构体类型的数据指针的方法跟定义指向简单类型变量的指针方法相同,只不过数据类型的声明部分用在程序中已定义的结构体数据类型来替代。如:structstudent{intnum;charname[20];floatscore;};structstudent*p,stu;跟任何其他类型的指针一样,要使用定义好的指针变量,在使用前应对它赋值,也就是使它确切地表示某一个变量的地址。例如有上面的定义,可对p进行如下赋值:p=&stu;如果已经定义了结构体数组变量,那么也可以用结构体数组名对同类型的结构体指针赋值。例如:structs