第八章构造类型第八章构造类型8.1概述8.2结构体8.3定义结构体类型变量的方法8.4结构体变量的引用8.5结构体变量的初始化8.6结构体数组8.7指向结构体类型数据的指针8.8链表8.9共用体8.10枚举类型8.11自定义类型标识符8.1概述在前面学习了一些基本的数据类型(也叫简单类型),如整型、实型、字符型等,这些类型都是系统定义好的,程序员可以直接拿来使用。对于复杂的客观实体的定义,系统没有提供。例如一个学生的学号、姓名、性别、出生日期、学习成绩、家庭住址等属性的整体描述。返回C语言提供了自定义数据类型的方法,通过自定义类型将不同类型的数据组合成一个有机的整体,这些数据在一个整体中是互相联系的。这种自定义的数据类型叫构造类型。实际上在前面已经学习了一种构造类型——数组,数组是具有相同数据类型的一组元素集合。除了数组之外还有结构体、共用体。8.1概述8.2结构体结构体类型就是将不同类型的数据组合成一个有机的整体,以便于引用。一个学生的属性:学号(num)、姓名(name)、性别(sex)、年龄(age)、成绩(score)、家庭住址(addr)返回C语言没有提供这种现成的数据类型,因此用户必须要在程序中建立所需的结构体类型。声明一个结构体类型的一般形式为:8.2结构体以学生实体为例,建立一个结构体类型:8.2结构体structstudent{intnum;charname[20];charsex;intage;floatscore;charaddr[30];};声明了一个叫student的结构体类型,它包括num、name、sex、age、score、addr等不同类型的数据项。注意:(1)结构体类型名为:structstudent,其中struct是定义结构体类型的关键字,用来定义变量的类型。(2)在{}中定义的变量我们叫做成员,其定义方法和前面变量定义的方法一样,只是不能忽略最后的分号。8.2结构体必须遵循先声明结构体类型,再定义结构体变量的原则。三种定义结构体变量的方法:(1)先声明结构体类型再定义结构体变量名格式如下:8.3定义结构体类型变量的方法返回8.3定义结构体类型变量的方法例如:structstudent{intnum;charname[20];charsex;intage;floatscore;charaddr[30];};structstudentstu1,stu2;说明:定义结构体变量时,结构体类型名中的struct不能省,不能变成:studentstu1,stu2。(2)在声明类型的同时定义变量格式如下:8.3定义结构体类型变量的方法例如:8.3定义结构体类型变量的方法structstudent{intnum;charname[20];charsex;intage;floatscore;charaddr[30];}stu1,stu2;(3)直接定义结构类型变量格式如下:8.3定义结构体类型变量的方法其特点是在声明时不出现结构体名。例如:struct{intnum;charname[20];charsex;intage;floatscore;charaddr[30];}stu1,stu2;关于结构体类型说明:①类型与变量是两个不同的概念,不能混淆。变量分配内存空间,类型不分配空间。②对结构体中的成员可以单独使用,相当于普通变量,引用方法后面具体讲述。③结构体中成员也可以是一个结构体变量。8.3定义结构体类型变量的方法structdate{intmonth;intday;intyear;};structstudent{intnum;charname[20];charsex;intage;structdatebirthday;/*birthday是structdate类型*/charaddr[30];}stu1,stu2;引用结构体变量要遵守如下规则:(1)不能将一个结构体变量作为一个整体进行输入输出(引用),而只能对结构体变量中的各个成员分别进行输入和输出(引用)。结构体变量成员引用格式:8.4结构体变量的引用“.”是成员(又叫分量)运算符,它的优先级最高。例如stu1.num=10001;返回(2)如果成员本身又属于一个结构体类型,则要用若干个成员运算符,一级一级地找到最低一级的成员。只能对最低的成员进行赋值或存取以及运算。例如:stu1.birthday.year=1983;(3)对结构体变量成员可以像普通变量一样进行各种运算。例如:stu1.num++;stu1.age+=2;(4)可以引用结构体变量成员的地址。也可以引用结构体变量的地址。8.4结构体变量的引用结构变量的初始化8.5结构体变量的初始化structstudent{Intnum;charname[20];charsex;Intage;charaddr[30];}stu1={10001,Liming,'M',18,HubeiEnshi};返回定义结构体数组在定义结构体数组之前必须先声明结构体类型。如前面定义的结构体类型structstudent。声明好结构体类型之后就可以定义结构体数组了,其方法和定义简单类型的数组相似:8.6结构体数组也可以直接定义结构体数组返回结构体数组的初始化8.6结构体数组structstudent{intnum;charname[20];charsex;intage;floatscore;charaddr[30];}stu[2]={{10001,Liming,'M',18,89.5,HubeiEnshi},{10002,ZhangJun,'F',17,98,HubeiYichang}};可以不给出数组长度,数组长度由初始化的数据决定。stu[]={{…},{…},{…}};8.6结构体数组指向结构体变量的指针和定义简单变量的指针变量一样定义指向结构体变量的指针变量。8.7指向结构体类型数据的指针用结构体指针变量引用结构体变量。返回例如:structstudentstu1,*sp;sp=&stu1;sp-num=10001;sp-sex='M';sp-age=18;sp-score=89.5;8.7指向结构体类型数据的指针用结构体变量和指向结构体的指针作函数参数(1)用结构体成员变量做实际参数与简单变量作实参一样,属于“值传递”方式,只是要注意形参与实参在类型上要保持一致。8.7指向结构体类型数据的指针(2)用结构体变量做实际参数注意:①结构体变量的传递采用的是“值传递”的方式。②形参与实参的类型必须相同。③形参在函数调用期间也要占用内存单元,因此这种传递方式在空间与时间上开销较大。8.7指向结构体类型数据的指针(3)指向结构体变量(或数组)的指针做实际参数用指向结构体变量(或数组)的指针做实参是经常采用的一种方法。形参指针和实参指针都指向同一存贮单元,这种特点为函数返回多个数据提供了途径。8.7指向结构体类型数据的指针8.8链表链表概述问题的提出:存放一个班级的学生信息,可以采用数组的方法,要存放30个学生就设计长度为30的数组,要存放50个学生就设计长度为50的数组。假如事先并不知道学生人数,就必须将数组设计得足够大,例如,设计长度为100的数组,但实际学生数只有30,这样就会造成内存的浪费。显然用数组只适合于已知长度的数据,因为数组对内存的占用是静态的,程序运行过程中数组的长度是不变的。返回链表是一种常见的重要的数据结构。它可以动态的分配存储空间,需要多少就分配多少,是一种动态地进行存储分配的结构。一种简单链表如图所示:8.8链表按规定在一个结构体的定义中,其成员类型可以是除本身结构类型之外的任何已有类型。也可以是任何已有类型(包括本身类型在内)的指针类型。结构体不能进行除指针类型数据成员的递归定义。这是因为无论什么类型的指针变量所占的空间是确定的,都是4个字节。8.8链表structF{floatdata;structF*next;};链表有一个“头指针”变量,图中以head表示,它存放一个地址,该地址指向一个“结点”(在链表中将元素称为“结点”),每个结点包括两部分:一部分是用户需要用的实际数据(如A、B、C、D),另一部分为下一个结点的地址。8.8链表head指向第一个结点,第一个结点指向第二个结点,直到最后一个结点,最后一个结点不再指向其他结点,称它为“表尾”,它的地址部分为“NULL”(表示“空地址”),链表到此结束。可以看到,这种链表的数据结构,必须利用指针特性才能实现。即一个结点中应包含一个指针变量,用它存放下一结点的地址。8.8链表声明结构体类型“结点”。structstudent{longnum;floatscore;structsrudent*next;};数据域中的数据采用给成员变量赋值的办法来赋值。指针域中的值是下一个结点的地址。8.8链表静态链表例8.6建立一个如上图所示的简单链表,它由3个学生数据的结点组成,输出结点中的数据。8.8链表动态链表C语言的库函数提供了开辟存贮空间的函数。(1)malloc函数原型为:8.8链表该函数如果成功调用,可以在内存中开辟size指定大小的连续空间。返回值类型为void,请注意这不是表示没有返回值,而是表示返回值可以指向任何类型。如果失败返回NULL。该函数返回值是void类型,因此调用时需要强制转换成需要的类型。如:(int*)malloc(sizeof(int));(structpeople*)malloc(sizeof(structstudent));(2)free函数原型为:8.8链表其作用是释放由p指向的内存区,即将这部分内存还给系统。要注意动态开辟的内存在不用之后应及时还给系统。free函数无返回值。实际上只有建立动态链表才是有意义的。例8.7写一个函数建立一个有若干个学生数据的单向动态链表,并用num是否为0来判断输入是否结束。8.8链表算法的N-S图如下:8.8链表建立动态链表的步骤如下:第一步:定义一个头指针head并指向NULL。第二步:开辟新结点,并使p1,p2指向它,然后输入一个学生数据给新结点,并使指针域指向NULL。8.8链表第三步:由于p1-num!=0,所以再开辟一个新结点,并使p1指向新结点,然后输入一个学生数据给新结点,并使指针域指向NULL。第四步:由于p1-num!=0,所以再开辟一个新结点,并使p1指向新结点,然后输入一个学生数据给新结点,并使指针域指向NULL。8.8链表第五步:由于p1-num!=0,所以再开辟一个新结点,并使p1指向新结点,然后输入一个学生数据给新结点,并使指针域指向NULL。8.8链表8.9共用体问题的提出:设计一个people结构体,用来存放学生或教师信息:学生和教师信息中前五项都可以相同(类型),但后二项就不同了,如果是学生就要填写整型的班级号,如果是教师就要填写字符串型的职称了。返回怎么解决上述问题,能否做到是学生只分配班级号空间,是教师就分配职称空间,共用体可以做到这一点,以达到节省内存和更符合实际的目的。共用体的概述这种使几个不同的变量共占同一段内存的结构,称为“共用体”类型。共用体也是属于构造类型,也要先声明类型再定义变量。8.9共用体第一、这些不同的变量它们起始地址都相同;第二、每次只能存放其中一个变量。也就是使用覆盖技术,几个变量互相覆盖。第三、共用体变量所占内存大小取几个不同类型变量所占内存中最大的那一个。8.9共用体共用体类型的声明8.9共用体例:uniondata{inti;chatch;floatf;}a,b,c;共用体变量的引用方法共用体变量的引用方法同结构体变量的引用方法一样,即不能整体引用共用体变量,只能引用共用体成员。格式:8.9共用体共用体类型的特点(1)同一个内存段可以用来存放几种不同类型的成员,但在某一时刻只能存放其中一种。(2)内存中存放的数据是最后一次存入的内容,在此之前的内容全部被冲掉(覆盖),因此起作用的成员是最后一次存放的成员。(3)共用体变量的地址和它的各成员的地址都是同一地址。8.9共用体(4)不