1、1第十章第十章 结构体与共用体结构体与共用体第一节第一节 概述概述第二节第二节 结构体的说明和引用结构体的说明和引用第三节第三节 结构体数组结构体数组第四节第四节 结构的指针结构的指针第五节第五节 用用typedef定义类型名定义类型名第六节第六节 链表链表第七节第七节 共用体共用体2第一节第一节 概述概述构造类型:数组、结构体、共用体、枚举类型构造类型:数组、结构体、共用体、枚举类型 构造类型数据是由基本类型数据按一定规则组合而成。构造类型数据是由基本类型数据按一定规则组合而成。数组是将同类型的数据组合在一起,数组适合描述不同对数组是将同类型的数据组合在一起,数组适合描述不同对象的同一属性。
2、象的同一属性。但在实际中常常需要描述同一对象的不同属性,将不但在实际中常常需要描述同一对象的不同属性,将不同类型的数据作为一个整体来处理,如:一个学生的基本同类型的数据作为一个整体来处理,如:一个学生的基本情况,它包括:学号、姓名、性别、出生日期、民族,在情况,它包括:学号、姓名、性别、出生日期、民族,在一个组合项中包含了若干个类型不同的数据项,它被称为一个组合项中包含了若干个类型不同的数据项,它被称为结构体。结构体。3第二节第二节 结构体的说明和引用结构体的说明和引用一、结构说明一、结构说明 结构说明的任务是定义结构的类型和用定义的结构类结构说明的任务是定义结构的类型和用定义的结构类型来说明
3、各种对象。结构说明有以下几种方式。型来说明各种对象。结构说明有以下几种方式。1.先定义结构类型,再用定义的结构类型定义变量先定义结构类型,再用定义的结构类型定义变量 struct 结构名结构名 成员项表列;成员项表列;struct 结构名结构名 变量名表列;变量名表列;4例如:例如:struct student int num;char name20;char sex;char date8;char nation;struct student stu1,stu2;这样这样stu1和和stu2就具有了就具有了struct student 类型的结构。类型的结构。struct student被称为
4、被称为“类型名类型名”,或,或“类型标识符类型标识符”。52.定义类型的同时定义变量定义类型的同时定义变量 struct 结构名结构名 成员项表列;成员项表列;变量名表列变量名表列;例如:例如:struct student int num;char name20;char sex;char date8;char nation;stu1,stu2;63.直接定义结构体变量直接定义结构体变量 struct 成员表列;成员表列;变量名表列;变量名表列;即不出现结构体名。即不出现结构体名。如:如:struct int num;char name20;char sex;char date8;char n
5、ation;stu1,stu2;7说明:说明:(1)一个结构体类型名由两个标识符组成:)一个结构体类型名由两个标识符组成:struct 结构名,结构名,如:如:struct student 为结构体类型名。为结构体类型名。(2)一个结构体类型由若干个数据项组成,每一个数据项都)一个结构体类型由若干个数据项组成,每一个数据项都属于一种已定义的类型,每一个数据项称为一个结构体的属于一种已定义的类型,每一个数据项称为一个结构体的成员,也称为域,而不能称作变量。成员,也称为域,而不能称作变量。(3)系统没有预先定义结构体类型,凡需使用结构体类型数)系统没有预先定义结构体类型,凡需使用结构体类型数据的,
6、都必须在程序中自己定义。因此,结构体类型并非据的,都必须在程序中自己定义。因此,结构体类型并非只有一种,而是有很多种。只有一种,而是有很多种。(4)定义一个结构体类型,并不意味着系统将分配一段内存)定义一个结构体类型,并不意味着系统将分配一段内存单元来存放各数据项成员。只有在定义结构体变量后,才单元来存放各数据项成员。只有在定义结构体变量后,才分配存储单元。分配存储单元。8(5)在内存中,结构体变量占据一片连续的存储单元,可以用在内存中,结构体变量占据一片连续的存储单元,可以用sizeofsizeof函数测出其长度。函数测出其长度。如:如:printf(“%dn”,sizeof(structp
7、rintf(“%dn”,sizeof(struct student);student);二、结构的初始化二、结构的初始化 结构体变量在说明时可以进行初始化,初值是由常量表达结构体变量在说明时可以进行初始化,初值是由常量表达式组成的初值表。例如:式组成的初值表。例如:struct int num;char name20;char sex;char date8;char nation;stu1=1001,”li dan”,F,“19840310”,H;9三、结构体变量的引用三、结构体变量的引用1.引用结构体变量中的成员引用结构体变量中的成员引用形式为:结构变量名引用形式为:结构变量名.成员名成员名
8、 其中其中“.”称为成员运算符,连接结构变量名和成员名,称为成员运算符,连接结构变量名和成员名,属于最高级运算符,所以结构成员的引用表达式在任何地属于最高级运算符,所以结构成员的引用表达式在任何地方出现都是一个整体。方出现都是一个整体。stu1.num=1001;stu1.sex=M scanf(“%s”,stu1.name);strcpy(stu1.name,”li ming”);102.整体引用结构体变量整体引用结构体变量 具有相同类型的结构变量的赋值。具有相同类型的结构变量的赋值。例如:例如:struct student stu2,stu1=1001,”li,dan”,F,”198403
9、10”,H;stu2=stu1;3.结构成员的输入输出结构成员的输入输出 C语言不允许把一个结构变量作为一个整体进行输入输出语言不允许把一个结构变量作为一个整体进行输入输出操作,而只能对其成员进行输入输出操作。操作,而只能对其成员进行输入输出操作。以下操作是错误的:以下操作是错误的:scanf(“%s”,&stu1);printf(“%s”,stu1);11正确的写法是:正确的写法是:printf(“%d,%s,%c,%s,%c”,stu1.num,stu1.printf(“%d,%s,%c,%s,%c”,stu1.num,stu1.name,stu1.sex,stu1.date,stu1.n
10、ation);name,stu1.sex,stu1.date,stu1.nation);scanf(“%d,%s,%c,%s,%c”,&stu1.num,stu1.scanf(“%d,%s,%c,%s,%c”,&stu1.num,stu1.name,&stu1.sex,stu1.date,&stu1.nationame,&stu1.sex,stu1.date,&stu1.nation);n);4.引用结构变量和成员的地址引用结构变量和成员的地址例如:例如:&stu1和和&stu1.num的地址相同,但是,其类型不同,的地址相同,但是,其类型不同,&stu1是结构地址,是结构地址,&stu1.n
11、um的地址是整型地址。的地址是整型地址。例例 10_112四、嵌套的结构四、嵌套的结构 结构的一个成员还可以是一个结构,含有结构成员的结构结构的一个成员还可以是一个结构,含有结构成员的结构称为嵌套的结构。如,称为嵌套的结构。如,student 结构可以改写为:结构可以改写为:struct date int year;int month;int day;struct student int num;char name20;char sex;struct date birthday;char nation;stu1=1001,”li dan”,F,1984,03,10,H;13第三节第三节 结构体
12、数组结构体数组 一个结构体变量只能存放一个对象(如:一个学生)的一个结构体变量只能存放一个对象(如:一个学生)的一组数据,如果要存入多个对象的有关数据就要使用数组,一组数据,如果要存入多个对象的有关数据就要使用数组,这就是结构体数组。结构体数组的每个元素都是一个结构体这就是结构体数组。结构体数组的每个元素都是一个结构体类型的变量。类型的变量。一、结构体数组的定义一、结构体数组的定义 与定义结构体变量相似,它也有三种定义方法。与定义结构体变量相似,它也有三种定义方法。1.先定义类型,再定义变量先定义类型,再定义变量 struct student 结构体成员;结构体成员;;struct stude
13、nt stu130;142.在定义结构体类型的同时定义结构体数组。在定义结构体类型的同时定义结构体数组。struct student 结构体成员;结构体成员;stu130;3.直接定义结构体变量而不定义类型名直接定义结构体变量而不定义类型名 struct 结构体成员;结构体成员;stu130;二、结构体数组的初始化二、结构体数组的初始化 在定义结构体数组的同时给数组赋值称为初始化。在定义结构体数组的同时给数组赋值称为初始化。如:如:struct student stu1=1001,”zhang”,F,”19840101”,H,1002,”li”,M,”19840501”,H;15三、结构体数组
14、的引用三、结构体数组的引用1.引用某一元素中的一个成员引用某一元素中的一个成员如:如:stu10.num stu1i.num2.可以将一个结构体数组元素赋值给同一结构体类型数组中的可以将一个结构体数组元素赋值给同一结构体类型数组中的另一个元素,或赋值给同一类型的变量。另一个元素,或赋值给同一类型的变量。如:如:struct student stu13,s1;stu10.num=1001;stu10.sex=M;stu11=stu10;s1=stu11;163.不能把结构体数组元素作为一个整体直接进行输入或输出,不能把结构体数组元素作为一个整体直接进行输入或输出,而只能以单个成员为对象进行输入或
15、输出。而只能以单个成员为对象进行输入或输出。如:以下是错误的操作如:以下是错误的操作 scanf(“%s”,stu1 0);printf(“%s”,stu10);以下操作是正确的:以下操作是正确的:for(ifor(i=0;i3;i+)=0;inum 相当于相当于 +(p-num)p-num+1 相当于相当于 (p-num)+1 p-num+相当于相当于 (p-num)+21第五节第五节 用用 typedef 定义类型名定义类型名 typedef是是C的一个关键字,语法地位与存储类型区分符的一个关键字,语法地位与存储类型区分符相同。相同。Typedef用于为已存在的数据类型定义一个别名。用于为
16、已存在的数据类型定义一个别名。格式:格式:typedef 数据类型数据类型 标识符;标识符;其中:其中:数据类型可以是任何标准数据类型或已定义过的类型。数据类型可以是任何标准数据类型或已定义过的类型。标识符就是用户为该数据类型所起的别名。标识符就是用户为该数据类型所起的别名。例如:例如:typedef int HIGH;HIGH a,b;22例如:例如:typedef struct student STUDENT;STUDENT stu1;或者,在定义结构类型时对该类型起别名:或者,在定义结构类型时对该类型起别名:typedef struct student 成员表列;成员表列;STUDENT
17、;typedef struct treenode char*word;int conint;struct treenode*left,*right;TREENODE;(数据结构中的二叉树)(数据结构中的二叉树)23第六节第六节 链表链表 24一、链表的概念一、链表的概念 链表是将若干个称为链表是将若干个称为“结点结点”的数据项通过指针首尾相连的数据项通过指针首尾相连而形成的数据整体。每个结点是相同类型的结构变量,由数据而形成的数据整体。每个结点是相同类型的结构变量,由数据域和指针域两部分组成,链表的连接原则是:前一个结点域和指针域两部分组成,链表的连接原则是:前一个结点“指指向向”下一个结点,
18、只有通过前一个结点才能找到下一个结点。下一个结点,只有通过前一个结点才能找到下一个结点。如图所示如图所示。该链表称为单向链表,有一个该链表称为单向链表,有一个“头指针头指针”变量,一般以变量,一般以head表示,它只存放一个地址,这个地址指向第一个结点。最表示,它只存放一个地址,这个地址指向第一个结点。最后一个结点因为没有后继结点,被称为后一个结点因为没有后继结点,被称为“表尾表尾”,它不再指向,它不再指向任何结点,它的地址为任何结点,它的地址为“NULL”。25 链表中各结点在内存中是可以不连续存放的。要找到链表中各结点在内存中是可以不连续存放的。要找到某一结点,必须顺序查找。如果不提供某一
19、结点,必须顺序查找。如果不提供“头指针头指针”即即head结点,则整个链表都无法访问。链表这种数据结构必须要结点,则整个链表都无法访问。链表这种数据结构必须要利用指针变量来实现。利用指针变量来实现。二、用包含指针项的结构体变量构成结点二、用包含指针项的结构体变量构成结点 从上面的介绍可以知道:链表中的每一个结点由两部从上面的介绍可以知道:链表中的每一个结点由两部分组成:数据域和指针域,包含指针项的结构体变量就是分组成:数据域和指针域,包含指针项的结构体变量就是一个结点,对于右图所示的结点可以用下面的数据类型来一个结点,对于右图所示的结点可以用下面的数据类型来表示:表示:struct stu_s
20、core int num;int score;struct stu_score *next;这种用同一类型的结点形成的链表这种用同一类型的结点形成的链表称为同质链表,是使用最普遍的链表。称为同质链表,是使用最普遍的链表。num score next26三、链表与动态存储三、链表与动态存储 以前我们在处理以前我们在处理“批量批量”数据时使用的是数组,数组数据时使用的是数组,数组的一个缺点是数组元素的个数必须事先定义,不能改变。的一个缺点是数组元素的个数必须事先定义,不能改变。而现在我们可以使用链表,链表的优点是每个存储单元都而现在我们可以使用链表,链表的优点是每个存储单元都是由动态存储分配获得,
21、即在程序执行过程中,根据需要是由动态存储分配获得,即在程序执行过程中,根据需要随时开辟存储单元,不再需要时又可随时释放。随时开辟存储单元,不再需要时又可随时释放。在在C语言中,存储单元的动态开辟和释放都是由语言中,存储单元的动态开辟和释放都是由C编编译系统提供的动态存储分配库函数来实现的。它们是:译系统提供的动态存储分配库函数来实现的。它们是:malloc()calloc()free()recalloc()271.malloc函数函数函数原型为:函数原型为:void *malloc(unsigned int size)其功能是在内存的动态存储区分配一个长度为其功能是在内存的动态存储区分配一个长
22、度为size的内存空的内存空间,并将其起始地址作为函数值带回,返回值是一个间,并将其起始地址作为函数值带回,返回值是一个void类型类型的指针,若分配不成功,则返回的指针,若分配不成功,则返回0值。值。2.calloc函数函数函数原型为:函数原型为:void *calloc(unsigned int n,unsigned int size)其作用是分配其作用是分配n个个size字节的内存空间,此函数的返回值为字节的内存空间,此函数的返回值为该空间的首地址,若分配不成功,则返回该空间的首地址,若分配不成功,则返回0值。值。283.free函数函数函数原型为:函数原型为:void free(voi
23、d *ptr)如:如:p=(struct st *)malloc(8);/*强制类型转换强制类型转换*/free(p);其作用是释放其作用是释放p所指向的存储空间。所指向的存储空间。4.realloc函数函数函数原型为:函数原型为:void *realloc(void*ptr,unsigned int size)其作用是使已分配的空间改变大小,即重新分配,该其作用是使已分配的空间改变大小,即重新分配,该函数返回值是新存储区的首地址。函数返回值是新存储区的首地址。如:如:realloc(p,10)/*重新分配的长度为重新分配的长度为10字节字节*/29四、链表的建立四、链表的建立 链表的建立是从
24、无到有、一个一个地输入各结点数据,最链表的建立是从无到有、一个一个地输入各结点数据,最后建立前后相链关系的一个过程,建立单向链表的主要操作步后建立前后相链关系的一个过程,建立单向链表的主要操作步骤如下:骤如下:(1)定义链表的数据结构;)定义链表的数据结构;(2)读取数据;)读取数据;(3)生成新结点;)生成新结点;(4)将数据存入结点的成员变量中;)将数据存入结点的成员变量中;(5)将新结点连接到表尾,重复)将新结点连接到表尾,重复(2)(5)的操作。如果数据输入结的操作。如果数据输入结束,则将新结点的指针成员赋值为空。束,则将新结点的指针成员赋值为空。例例 10_11 编写函数编写函数cr
25、eatlist,建立一个单向链表。结点数据域,建立一个单向链表。结点数据域的数值从键盘输入,以的数值从键盘输入,以-1作为输入的结束标志。链表的起始地作为输入的结束标志。链表的起始地址由函数值返回。址由函数值返回。30五、链表的操作五、链表的操作 对链表的操作包括:对链表的操作包括:(1)链表的输出链表的输出 (2)在链表中插入结点在链表中插入结点 (3)删除链表中的结点删除链表中的结点 1.链表的输出链表的输出 输出链表是指将链表各结点的数据依次输出。单向链表的输出链表是指将链表各结点的数据依次输出。单向链表的输出可以如下进行:输出可以如下进行:(1)首先找到链表头元素的地址即首先找到链表头
26、元素的地址即head 的值;的值;(2)定义定义一个指针变量并使它指向第一个结点,输出其值成员;一个指针变量并使它指向第一个结点,输出其值成员;(3)使指针变量指向下一个结点,再输出其值成员。重复此步直使指针变量指向下一个结点,再输出其值成员。重复此步直到链表的尾结点。到链表的尾结点。例例 10_12 建立一个单向链表,并输出其值。建立一个单向链表,并输出其值。312.2.在链表中插入结点在链表中插入结点 在链表中插入一个结点的操作是指在链表中的任意位置添加在链表中插入一个结点的操作是指在链表中的任意位置添加一个新结点。一个新结点。例例 10_13 试编写一个在一个已存在(不为空)的升序链表中
27、插试编写一个在一个已存在(不为空)的升序链表中插入一个值为入一个值为x的结点的函数的结点的函数insenode。说明:将说明:将creatlist 函数和函数和display 函数加在本例的前面。函数加在本例的前面。3.3.删除链表中的结点删除链表中的结点 删除链表中结点是指将链表中指定的某个结点从链表中分离删除链表中结点是指将链表中指定的某个结点从链表中分离开来。如图所示。开来。如图所示。例例 10_14 编写一个删除链表中值为编写一个删除链表中值为x的结点的函数。的结点的函数。324.4.有关链表特点的总结有关链表特点的总结(1)链表与数组都可以用来存储数据,但数组所占的内存区大)链表与数
28、组都可以用来存储数据,但数组所占的内存区大小是固定的,而链表则是不固定的,可以随时增减,数组占小是固定的,而链表则是不固定的,可以随时增减,数组占连续一片内存区,而链表则不同,它用指针指向下一个结点,连续一片内存区,而链表则不同,它用指针指向下一个结点,各结点在内存中的位置可以是任意的。各结点在内存中的位置可以是任意的。(2)链表中每一结点内必须包含一个指针数据项,以使它指向)链表中每一结点内必须包含一个指针数据项,以使它指向下一结点。如果没有此指针项就不能形成下一结点。如果没有此指针项就不能形成“链链”。(3)要动态地开辟单元,必须用)要动态地开辟单元,必须用malloc或或calloc函数
29、。所开辟函数。所开辟的结点是结构体类型的,但无变量名,只能用间接方法(指的结点是结构体类型的,但无变量名,只能用间接方法(指针方法)访问它。针方法)访问它。33(4)对单向链表中结点的访问只能从)对单向链表中结点的访问只能从“头指针头指针”开始。如果没有开始。如果没有“头指针头指针”,就无法进入链表,也无法访问其中各个结点。对,就无法进入链表,也无法访问其中各个结点。对单向链表中结点的访问只能顺序进行。单向链表中结点的访问只能顺序进行。(5)单向链表中最后一个结点中的指针域必须是)单向链表中最后一个结点中的指针域必须是NULL,不指向任,不指向任何结点。何结点。(6 6)如果断开链表中某一处的
30、链,则其后的结点都将)如果断开链表中某一处的链,则其后的结点都将“失去联失去联系系”,它们虽然在内存中存在,但无法访问到它们。,它们虽然在内存中存在,但无法访问到它们。34第七节第七节 共用体(联合)共用体(联合)一、共用体的概念一、共用体的概念 共用体(共用体(union)又称为联合体,是一种构造数据类型,它是)又称为联合体,是一种构造数据类型,它是将不同类型的数据项存放于同一段内存单元中。将不同类型的数据项存放于同一段内存单元中。二、共用体的定义二、共用体的定义 共用体的定义与结构体相似,可以有以下三种形式:共用体的定义与结构体相似,可以有以下三种形式:1union 共用体名共用体名 成员
31、表列成员表列 ;union 共用体名共用体名 变量表列;变量表列;2union 共用体名共用体名 成员表列成员表列 变量表列变量表列;353union 成员表列成员表列 变量表列变量表列;例如:例如:union un1 int x;char y;float z;t1,t2,*q;共用体变量的长度为其成员中最长的成员长度,共用体变量的长度为其成员中最长的成员长度,而结构体变量的长度是其成员项长度之和。而结构体变量的长度是其成员项长度之和。yxz36注意:注意:(1)不能在定义共用体变量时对它初始化不能在定义共用体变量时对它初始化。如:以下操作是错误的。如:以下操作是错误的。union un1 i
32、nt x;char y;float z;t1=2,w,3.5;联合成员彼此不是并存的,任一时刻联合变量中只含有其联合成员彼此不是并存的,任一时刻联合变量中只含有其中一个成员,该成员是最近一次存入联合的那一个成为当前成中一个成员,该成员是最近一次存入联合的那一个成为当前成员。员。(2)共用体变量的地址也就是该变量成员的地址。共用体变量的地址也就是该变量成员的地址。如:如:&t1=&t1.x=&t1.y37三、共用体变量的引用三、共用体变量的引用 1.引用共用体变量中的成员引用共用体变量中的成员 引用共用体变量成员的方式与结构体完全相同。引用共用体变量成员的方式与结构体完全相同。如:如:union
33、 un1 int x;char y;float z;t1,t2,*p;p=&t1;一个共用体变量不是同时存放多个成员的值,而一个共用体变量不是同时存放多个成员的值,而是只能存放其中的一个值,即最后赋予它的值。是只能存放其中的一个值,即最后赋予它的值。如:如:t1.x=6;t1.y=M;t1.z=3.4;t1.x=6;t1.y=M;t1.z=3.4;则共用体变量则共用体变量t1t1中存放的有效值为中存放的有效值为3.43.4。382.2.共用体变量的整体赋值共用体变量的整体赋值 如:如:union un1 t1,t2;union un1 t1,t2;t1.x=6;t1.x=6;t2=t1;t2=
34、t1;则则t2.xt2.x的值也是的值也是6 6。例例 10_15 将下列数据组说明为共用体类型,并进行赋值和将下列数据组说明为共用体类型,并进行赋值和显示。显示。姓姓 名名 年令年令 月收入月收入 wanghua 38 120039四、共用体变量的应用四、共用体变量的应用 共用体变量可以增加程序的灵活性,对同一段内存共用体变量可以增加程序的灵活性,对同一段内存空间的值在不同情况下作不同的用处。至少有两方面的用空间的值在不同情况下作不同的用处。至少有两方面的用途。途。在数据处理问题中,常用一个数据域存放不同的对象。在数据处理问题中,常用一个数据域存放不同的对象。例:一个学校的人员数据管理中,对教师则应登记其例:一个学校的人员数据管理中,对教师则应登记其“单单位位”,对学生则应登记其,对学生则应登记其“班级班级”,它们都有在同一栏中。,它们都有在同一栏中。