1第十章结构体与共用体第一节概述第二节结构体的说明和引用第三节结构体数组第四节结构的指针第五节用typedef定义类型名第六节链表第七节共用体2第一节概述构造类型:数组、结构体、共用体、枚举类型构造类型数据是由基本类型数据按一定规则组合而成。数组是将同类型的数据组合在一起,数组适合描述不同对象的同一属性。但在实际中常常需要描述同一对象的不同属性,将不同类型的数据作为一个整体来处理,如:一个学生的基本情况,它包括:学号、姓名、性别、出生日期、民族,在一个组合项中包含了若干个类型不同的数据项,它被称为结构体。3第二节结构体的说明和引用一、结构说明结构说明的任务是定义结构的类型和用定义的结构类型来说明各种对象。结构说明有以下几种方式。1.先定义结构类型,再用定义的结构类型定义变量struct结构名{成员项表列;};struct结构名变量名表列;4例如:structstudent{intnum;charname[20];charsex;chardate[8];charnation;};structstudentstu1,stu2;这样stu1和stu2就具有了structstudent类型的结构。structstudent被称为“类型名”,或“类型标识符”。52.定义类型的同时定义变量struct结构名{成员项表列;}变量名表列;例如:structstudent{intnum;charname[20];charsex;chardate[8];charnation;}stu1,stu2;63.直接定义结构体变量struct{成员表列;}变量名表列;即不出现结构体名。如:struct{intnum;charname[20];charsex;chardate[8];charnation;}stu1,stu2;7说明:(1)一个结构体类型名由两个标识符组成:struct结构名,如:structstudent为结构体类型名。(2)一个结构体类型由若干个数据项组成,每一个数据项都属于一种已定义的类型,每一个数据项称为一个结构体的成员,也称为域,而不能称作变量。(3)系统没有预先定义结构体类型,凡需使用结构体类型数据的,都必须在程序中自己定义。因此,结构体类型并非只有一种,而是有很多种。(4)定义一个结构体类型,并不意味着系统将分配一段内存单元来存放各数据项成员。只有在定义结构体变量后,才分配存储单元。8(5)在内存中,结构体变量占据一片连续的存储单元,可以用sizeof函数测出其长度。如:printf(“%d\n”,sizeof(structstudent));二、结构的初始化结构体变量在说明时可以进行初始化,初值是由常量表达式组成的初值表。例如:struct{intnum;charname[20];charsex;chardate[8];charnation;}stu1={1001,”lidan”,’F’,“19840310”,’H’};9三、结构体变量的引用1.引用结构体变量中的成员引用形式为:结构变量名.成员名其中“.”称为成员运算符,连接结构变量名和成员名,属于最高级运算符,所以结构成员的引用表达式在任何地方出现都是一个整体。stu1.num=1001;stu1.sex=‘M’scanf(“%s”,stu1.name);strcpy(stu1.name,”liming”);102.整体引用结构体变量具有相同类型的结构变量的赋值。例如:structstudentstu2,stu1={1001,”li,dan”,’F’,”19840310”,’H’};stu2=stu1;3.结构成员的输入输出C语言不允许把一个结构变量作为一个整体进行输入输出操作,而只能对其成员进行输入输出操作。以下操作是错误的:scanf(“%s”,&stu1);printf(“%s”,stu1);11正确的写法是:printf(“%d,%s,%c,%s,%c”,stu1.num,stu1.name,stu1.sex,stu1.date,stu1.nation);scanf(“%d,%s,%c,%s,%c”,&stu1.num,stu1.name,&stu1.sex,stu1.date,&stu1.nation);4.引用结构变量和成员的地址例如:&stu1和&stu1.num的地址相同,但是,其类型不同,&stu1是结构地址,&stu1.num的地址是整型地址。[例10_1]12四、嵌套的结构结构的一个成员还可以是一个结构,含有结构成员的结构称为嵌套的结构。如,student结构可以改写为:structdate{intyear;intmonth;intday;};structstudent{intnum;charname[20];charsex;structdatebirthday;charnation;}stu1={1001,”lidan”,’F’,1984,03,10,’H’};13第三节结构体数组一个结构体变量只能存放一个对象(如:一个学生)的一组数据,如果要存入多个对象的有关数据就要使用数组,这就是结构体数组。结构体数组的每个元素都是一个结构体类型的变量。一、结构体数组的定义与定义结构体变量相似,它也有三种定义方法。1.先定义类型,再定义变量structstudent{结构体成员;};structstudentstu1[30];142.在定义结构体类型的同时定义结构体数组。structstudent{结构体成员;}stu1[30];3.直接定义结构体变量而不定义类型名struct{结构体成员;}stu1[30];二、结构体数组的初始化在定义结构体数组的同时给数组赋值称为初始化。如:structstudentstu1[]={{1001,”zhang”,’F’,”19840101”,’H’},{1002,”li”,’M’,”19840501”,’H’}};15三、结构体数组的引用1.引用某一元素中的一个成员如:stu1[0].numstu1[i].num2.可以将一个结构体数组元素赋值给同一结构体类型数组中的另一个元素,或赋值给同一类型的变量。如:structstudentstu1[3],s1;stu1[0].num=1001;stu1[0].sex=‘M’;stu1[1]=stu1[0];s1=stu1[1];163.不能把结构体数组元素作为一个整体直接进行输入或输出,而只能以单个成员为对象进行输入或输出。如:以下是错误的操作scanf(“%s”,stu1[0]);printf(“%s”,stu1[0]);以下操作是正确的:for(i=0;i3;i++){scanf(“%d,%s,%c,%s,%c”,&stu1[i].num,stu1[i].name,&stu1[i].sex,stu1[i].date,&stu1[i].nation);}17[例10_18]学生资料的输入输出。[例10_2]输入10个人的姓名和年令并存放在结构体数组中,试编程从中查找并输出年令最大的人的姓名和年令。18第四节结构的指针指向结构体变量的指针,称为结构体指针,或结构指针。结构指针可以用来引用结构体成员,或作为参数传递给函数。一、结构指针的定义与结构体变量的定义方法相同,有三种方法。例如:structstudent{结构体成员;};structstudentstu1,*p;p=&stu1;19二、用结构指针引用结构体成员如果p=&stu1,则*p等同于结构变量stu1,那么:stu1.num与(*p).num等价(*p).num=1002;(*p).sex=‘f’;注意:()不能省略。思考:下列写法是否正确:(*p).name=“lidan”;scanf(“%s”,p);20三、“–﹥”运算符–﹥被称为指向运算符,利用它可简化用指针引用结构成员的形式。这样可用三种形式来引用结构体变量的成员:1.stu1.num2.(*p).num3.p–﹥num以上三种形式是等价的。注意:“–﹥”运算符的优先级最高,因此:++p-num相当于++(p-num)p-num+1相当于(p-num)+1p-num++相当于(p-num)++21第五节用typedef定义类型名typedef是C的一个关键字,语法地位与存储类型区分符相同。Typedef用于为已存在的数据类型定义一个别名。格式:typedef数据类型标识符;其中:数据类型可以是任何标准数据类型或已定义过的类型。标识符就是用户为该数据类型所起的别名。例如:typedefintHIGH;HIGHa,b;22例如:typedefstructstudentSTUDENT;STUDENTstu1;或者,在定义结构类型时对该类型起别名:typedefstructstudent{成员表列;}STUDENT;typedefstructtreenode{char*word;intconint;structtreenode*left,*right;}TREENODE;(数据结构中的二叉树)23第六节链表24一、链表的概念链表是将若干个称为“结点”的数据项通过指针首尾相连而形成的数据整体。每个结点是相同类型的结构变量,由数据域和指针域两部分组成,链表的连接原则是:前一个结点“指向”下一个结点,只有通过前一个结点才能找到下一个结点。如图所示。该链表称为单向链表,有一个“头指针”变量,一般以head表示,它只存放一个地址,这个地址指向第一个结点。最后一个结点因为没有后继结点,被称为“表尾”,它不再指向任何结点,它的地址为“NULL”。25链表中各结点在内存中是可以不连续存放的。要找到某一结点,必须顺序查找。如果不提供“头指针”即head结点,则整个链表都无法访问。链表这种数据结构必须要利用指针变量来实现。二、用包含指针项的结构体变量构成结点从上面的介绍可以知道:链表中的每一个结点由两部分组成:数据域和指针域,包含指针项的结构体变量就是一个结点,对于右图所示的结点可以用下面的数据类型来表示:structstu_score{intnum;intscore;structstu_score*next;}这种用同一类型的结点形成的链表称为同质链表,是使用最普遍的链表。numscorenext26三、链表与动态存储以前我们在处理“批量”数据时使用的是数组,数组的一个缺点是数组元素的个数必须事先定义,不能改变。而现在我们可以使用链表,链表的优点是每个存储单元都是由动态存储分配获得,即在程序执行过程中,根据需要随时开辟存储单元,不再需要时又可随时释放。在C语言中,存储单元的动态开辟和释放都是由C编译系统提供的动态存储分配库函数来实现的。它们是:malloc()calloc()free()recalloc()271.malloc函数函数原型为:void*malloc(unsignedintsize)其功能是在内存的动态存储区分配一个长度为size的内存空间,并将其起始地址作为函数值带回,返回值是一个void类型的指针,若分配不成功,则返回0值。2.calloc函数函数原型为:void*calloc(unsignedintn,unsignedintsize)其作用是分配n个size字节的内存空间,此函数的返回值为该空间的首地址,若分配不成功,则返回0值。283.free函数函数原型为:voidfree(void*ptr)如:p=(structst*)malloc(8);/*强制类型转换*/free(p);其作用是释放p所指向的存储空间。4.realloc函数函数原型为:void*realloc(void*ptr,unsignedintsize)其作用是使已分配的空间改变大小,即重新分配,该函数返回值是新存储区的首地址。如:realloc(p,10)/*重新分配的长度为10字节*/29四、链表的建立链表的建立是从无到有、一个一个地输入各结点数据,最后建立前后相链关系的一个过程,建立单向链表的主要操作步骤如下:(1)定义链表的数据结构;(2)读取数据;(3)生成新结