320 likes | 507 Vues
第 9 章 构造数据类型. 1. 概述. 前面已介绍了基本类型 ( 即整型、实型、字符型等 ) 的变量,还介绍了构造类型 — 数组,而数组中的元素是属于同一类型的。. 但在实际应用中,有时需要将一些有相互联系而类型不同的数据组合成一个有机的整体,以便于引用。如学生学籍档案中的学号、姓名、性别、年龄、成绩、地址等数据,对每个学生来说,除了其各项的值不同外,但表示形式是一样的。. 这种多项组合又有内在联系的的数据称为 结构体 ( structure ) 。它是可以由用户自己定义的。. 9.1. 结构体类型. 9.1.1 结构体类型的定义形式.
E N D
第9章 构造数据类型 1. 概述 前面已介绍了基本类型(即整型、实型、字符型等)的变量,还介绍了构造类型 — 数组,而数组中的元素是属于同一类型的。 但在实际应用中,有时需要将一些有相互联系而类型不同的数据组合成一个有机的整体,以便于引用。如学生学籍档案中的学号、姓名、性别、年龄、成绩、地址等数据,对每个学生来说,除了其各项的值不同外,但表示形式是一样的。 这种多项组合又有内在联系的的数据称为结构体(structure)。它是可以由用户自己定义的。
9.1. 结构体类型 9.1.1 结构体类型的定义形式 struct 结构体名 { 分量表 }; 其中“分量表”中的分量也应进行类型说明,即: 类型标识符 分量名; 例如: structstudent {int num; char name[20]; char sex; int age; float score; char addr[30]; }; 两者缺一不可
由用户定义的“结构体类型”,可以同标准类型一样作为定义变量的类型。相当于PASCAL语言中的记录(record)。由用户定义的“结构体类型”,可以同标准类型一样作为定义变量的类型。相当于PASCAL语言中的记录(record)。 9.1.2 定义结构体类型变量的方法 一、 先定义结构体类型再定义变量名 定义了结构体类型struct student 后,可以用它定义变量。如: struct student st1, st2; 注:不能写成struct st1,st2;必须同时指定结构体名。 为了方便起见,可以在程序开头定义符号常量进行简化。如:
#define STUDENT struct student 则在程序中可以直接写成: STUDENT {int num; char name[20]; char sex; int age; float score; char addr[30]; }; STUDENT st1, st2;
二、在定义类型的同时定义变量 如:struct student {int num; char name[20]; char sex; int age; float score; char addr[30]; }st1,st2; 则一般定义形式为: struct 结构体名 { 分量表; } 变量表;
三、直接定义结构类型变量 定义形式为:struct {分量表;} 变量表; 关于结构体类型的几点说明 类型与变量是两个不同的概念。一般先定义结构体类型,再定义变量为该类型。变量可以赋值、存取或运算,而类型没有这些操作。在编译时,对变量分配空间,对类型来说不存在分配空间。 对结构体中的分量可以单独使用。 在 struct 后不出现结构体名,因此也不能再以此定义相同的结构体变量。
分量也可以是一个结构体变量。如 student 中要增加 birthday,则可按如下方式进行定义: struct date {int month; int day; int year; }; struct student { struct date birthday; }st1, st2; 分量名可以与程序中的变量名相同,两者之间不会产生混淆。
9.1.3 结构体类型变量的引用 引用结构体变量应遵守如下规则: 1) 结构体变量中分量的引用方式为: 结构体变量名分量名[ 二级分量名 ] 其中:“”为分量运算符, 在所有的运算符中优先级最高。 2) 结构体变量的分量本身又属于结构体类型时只能对最低级分量进行操作。如: st1.num、st1.name、st1.birthday.day。 st1.birthday并不会访问st1中的birthday,只会引起警告错误。
3) 只能对结构体变量的各分量进行输入输出,不能将一个结构体变量直接进行输入输出。 如:scanf(“%d,%s,%c,%d,%f,%s”,&st1); 错误printf(“%d,%s,%c,%d,%f,%s”,st1); 错误printf(“%s,%d”,st1.name,st1.birthday.day); 正确 4) 分量和结构体变量的地址均可以被引用。如: scanf(“%d”,&st1.num); (输入st1.num的值) printf(“%x”,&st1); (以十六进制输出st1的首地址)
9.1.4 结构体变量的初始化 1) 外部存储类的结构体变量初始化 例9.1 struct student {long int num; char name[20]; char sex; char addr[30]; }a={89031,“Li Lin”,‘M’,“123 Beijing Road”}; main( ) {printf(“%ld,%s,%c,%s\n”, a.num,a.name,a.sex,a.addr); } 输出结果: 89031,Li Lin,M,123 Beijing Road
2) 静态存储类的结构体变量初始化 main( ) {struct student (可以将定义部分放在main函数中) { long int num; char name[20]; char sex; char addr[30]; }a={89031,“Li Lin”,‘M’,“123 Beijing Road”}; printf(“%ld,%s,%c,%s\n”, a.num,a.name,a.sex,a.addr); }
9.2 结构体数组 结构体数组与普通数组的不同之处在于每个数组元素都是一个结构体类型的数据,且这些数据又分别包括各个分量。结构体数组的定义、初始化等操作和内存中的存放方式与普通数组相类似。 9.3 指向结构体类型数据的指针 同普通变量一样,也可以定义一个指针变量指向一个结构体变量,则此时该指针变量的值是结构体变量的起始地址。指针变量也可以用来指向结构体数组中的元素,同样也可以用指向结构体的指针作函数参数。
例9.2 用指向结构体的指针作函数参数 #include “string.h” main( ) { struct student {long int num; char name[20]; char sex; float score; }; struct student stu; struct student *p; p=&stu; stu.num=89101; strcpy(stu.name,“Li Lin” ); stu.sex=‘M’; stu.score=89.5; printf(“No.:%d\nname:%s\nsex:%c\nscore:%f \n”, stu.num,stu.name,stu.sex,sru.score); printf(“\nNo.:%d\nname:%s\nsex:%c\nscore:%f \n”, (*p).num,(*p).name,(*p).sex,(*p).score); }
注意: (*p)表示p指向的结构体变量,不得省去括号。而 *p.num 等价于 *(p.num)。 (*p).num可写成 p–>num,使之直观,余类推。“–>”称为指向运算符。 结构体变量 分量名、(*p) 分量名、p–>分量名,三者是等价的。 p–>n 得到p指向的结构体变量中的分量n的值。 p–>n++ 得到p指向的结构体变量中的分量n的值,用完该值后加1。 ++p–>n 得到p指向的结构体变量中的分量n的值,并在用该值前先加1。
9.4 共用体(联合体) 一、 共用体的概念 二个以上不同类型的变量采用“覆盖技术”占用同一段内存单元的结构称为共用体。 共用体类型变量的定义形式如下: union 共用体名 {分量表 }变量表;
说明:虽然“共用体”与“结构体”的定义形式相似,但是:说明:虽然“共用体”与“结构体”的定义形式相似,但是: 一个结构体变量所需的存储容量为每个分量所需存储容量之和。而一个共用体变量所需的存储容量为各个分量中占用存储容量最多的分量所需的存储容量。 一个结构体变量的各个分量在任何时刻都同时存在,且可同时引用。而一个共用体变量的各个分量在同一时刻只存在其中一个,也只能引用其中的一个分量。即起作用的只是最后一次存放的分量,在存入一个新的分量后,原有分量的值被覆盖而失去作用。 一个结构体变量的各个分量的地址各不相同, 分别拥有各自的存储空间。而一个共用体变量的各个分量的地址相同,共同拥有同一存储空间。 共用体变量可作为参数传递给函数,也可以作函数的返回值。同样,可以使用地址传送方式将共用体变量的地址作为参数或返回值在函数间传递。
共用体类型可以出现在结构体类型定义中,也可以定义共用体类型数组,数组也可以作为共用体的分量。同样,结构体类型也可以出现在共用体类型定义中。共用体类型可以出现在结构体类型定义中,也可以定义共用体类型数组,数组也可以作为共用体的分量。同样,结构体类型也可以出现在共用体类型定义中。 不能在定义共用体变量时对其初始化,也不能对共用体变量名赋值,更不能企图引用共用体变量名去得到分量的值。 二、 共用体变量的引用 不能引用共用体变量,只能采用分量运算符 “ ” 引用共用体变量的分量。与引用结构体变量的方法是一致的。
通常,在定义嵌套有共用体变量的结构体变量时,在其中附加一个类型标志,以方便对共用体分量的操作。如:通常,在定义嵌套有共用体变量的结构体变量时,在其中附加一个类型标志,以方便对共用体分量的操作。如:
9.5 动态数据结构 静态数据结构(例如数组)占据内存空间的位置和大小是在它们被说明的同时由系统分配的, 在程序运行期间是不变的, 因此可以有效地访问它们的任何一个元素。但要删除和插入一个元素则比较困难, 往往要引起大量的数据移动。 而且数据量的扩充更受到它们所占用的有限内存空间的限制。C中的动态数据结构有效地解决了这一问题。动态数据结构中的每个组成数据在逻辑上是连续排列的,但在物理上即在内存中存储时并不占用连续的内存空间,它们可以根据需要随机地增加或减少其元素, 相应地占用或释放内存空间。 动态数据结构中最基本的形式是链表和二叉树, 它们在数据处理中起着十分重要的作用。
一、 动态存储分配 C语言实现动态存储分配的函数: malloc(size) 在内存的动态存储区中分配一个结点长度为size的连续存储空间,并返回一个指向其起始地址的指针,若分配不成功,则返回值为0。size为整型。 calloc(n, size) 在内存的动态存储区中分配n个结点长度为size的连续存储空间,并返回一个指向其起始地址的指针,若分配不成功,则返回值为0。n、size为整型。 free(ptr) 释放由指针ptr所指向的存储空间。ptr是最近一次调用malloc或calloc函数或链表指针返回的值。ptr为字符型指针。
二、 链表 链表概念 链表是一种常见的动态地进行存储分配的数据结构。链表有 “单向链表”、“双向链表”、“循环链表”、“双向循环链表”之分。下图是一个“单向链表”的示例。 单向链表是按照输入数据的顺序建立的。它有一个“头指针”(图中为head),指向第一个元素;每一个元素称为“结点”,每个结点包括两个域:数据域和指下一个结点的指针域;最后一个元素的指针域为 “NULL”(“空地址”),表示链表的结束,称为“表尾”。
p p p p p p p p p p head rear rear rear rear rear rear rear rear rear rear 建立链表 例9.3 链表的建立和遍历(队列) 恢复指针 2 3 4 5 6 7 8 9 10 1 0
删除链表元素 例9.5 删除链表中指定的结点。
对链表的插入操作 例9.6 在链表中插入一个新结点。
9.6 枚举类型 所谓“枚举”是指变量的取值只限于所列举出来的值的范围内。 枚举类型的定义以enum开头。如: enum weekday {sun,mon,tue,wed,thu,fri,sat}; enum weekday workday,week_end; enum weekday {sun,mon,tue,wed,thu,fri,sat}workday; 说明: { }中的枚举元素是常量而不是变量,也不代表什么实际的含义。 枚举型变量 workday, week_end 的取值只限于{ }中列举的元素范围内。 { }中枚举元素的值按其排列顺序为0、1、2、 ,可用于输出。
枚举值可按其定义时的顺序号用作判断比较。枚举值可按其定义时的顺序号用作判断比较。 不得直接将一个整数赋给一个枚举变量。如: Workday=2 ; 是不对的,因为它们不属于同一数据类型。 但可以进行强制类型转换赋值。如: workday=(enum weekday)2; 甚至可以是表达式,如: workday=(enum weekday)(5-3); 可用如下定义改变枚举元素中的序号值: enum weekday {sun,mon,tue,wed,thu=7,fri,sat}; 则枚举元素的序号值依次为: 0、1、2、3、7、8、9。
例9.7 口袋中有若干个红、黄、蓝、白、黑五种颜色的球,试编程求出每次从口袋中取出三个不同颜色的球的可能取法,并输出每种组合的三种颜色。
9.7 用 typedef 定义类型名 使用关键字typedef说明一个新的类型名,往往可以在程序中简化变量的类型定义。 例如: typedef struct student { int num; }REC; REC x , y , *p ; /相当于struct student x,y,*p;/ 语句: p=(struct student *)malloc(sizeof(struct student)); 可以写成:p=(REC *) malloc(sizeof(REC) )
说明: 用typedef 不是也不能建立新的数据类型,也不能用来定义变量,只是以一个新的类型名(通常用大写字母表示)代替已存在的类型名,以此简化程序中变量的类型定义。 使用typedef 有利于程序的通用性和可移植性。例如: 程序中有:int a,b,c;要修改为 long a,b,c; 则可用 typedef 定义: typedef int INTEGER; 在程序中用INTEGER定义变量,当修改程序时再用typedef 定义即可: typedef long INTEGER;
本章习题 P237 9.32 9.33 9.34 P240 9.38