C语言讲义第07章-结构体与其他构造数据类型(原).ppt
第7章结构体与其他构造数据类型,C语言程序设计,主要内容,结构体 结构体数组的定义和引用 指向结构体的指针结构体与函数 复杂的结构体 链表 共用体 位域枚举类型 类型定义typedef 小结,7.1结构体,表 71学生信息表,7.1结构体,结构体类型的定义形式:struct 结构体类型名 数据类型 成员1;数据类型 成员2;数据类型 成员n;,7.1结构体,例如:表7-1对应的结构体类型定义可以是:struct stu_infochar no8;/*学号*/char name10;/*姓名*/char sex;/*性别*/int age;/*年龄*/char department20;/*院系*/;,7.1结构体,结构体变量定义有以下三种形式:先定义结构体类型,再定义结构体类型变量 例如:在结构体类型定义完成后,再定义变量。struct stu_rec/*定义学生记录结构体类型*/char num8;/*学号*/char name10;/*学生姓名*/char sex;/*性别*/int score4;/*四科考试成绩*/;struct stu_rec student1,student2;/*定义结构体类型变量*/,7.1结构体,定义结构体类型的同时定义结构体类型变量例如:在定义结构体类型时定义变量。struct dateint year;int month;int day;mydate1,mydate2;,7.1结构体,直接定义结构体类型变量 例如:结构体类型定义时不指定类型名,而直接定义变量。struct char no8/*编号*/char name10;/*姓名*/char sex;/*性别*/float b_salary,f_salary,p_salary;/*基本工资、活工资、奖金*/person1,person2;/*定义该结构体类型变量*/,7.1结构体,结构体变量一旦进入其作用域,系统便根据结构体类型定义时成员排列的先后,自动为结构体变量的每一个成员分配相应的存储空间。结构体变量的各个成员均有自己的存储空间,结构体变量所占存储空间的大小为各成员所占空间之和。例如:student1 所占空间大小为:8+10+1+4*2=27(字节)。mydate1所点空间大小为:2+2+2=6(字节)。person1所占空间大小为:8+10+1+3*4=31(字节)。,另外,还可以直接使用sizeof()关键字来计算结构体变量的大小。例如:printf(%d,%d n,sizeof(student1),sizeof(person1);该语句的输出结果为:27,31,7.1结构体,C语言中,对结构体变量的输入、输出、赋值和运算等操作一般都是通过结构体变量的成员引用来实现的。结构体变量的成员引用,可使用成员运算符“.”来引用。引用的形式为:.若定义的结构体类型及变量如下:struct dateint day;int month;int year;today;则变量today各成员的引用形式为:today.day、today.month、today.year。,7.1结构体,结构体变量的初始化与一维基本类型数组的初始化方法相似。不同的是,对一维数组来说,是初始化数组元素(各元素类型相同),对结构体变量,则是初始化结构体成员(各成员类型可能不同)。,由于结构体类型变量可以汇集各类不同数据类型的成员,所以结构体类型变量的初始化必须在结构体类型变量定义时进行。例如:stu_info结构体类型定义如前所述,该类型变量的初始化形式为:struct stu_info student=06050113,li ping,f,20,computer science;,7.1结构体,也可以使用标准的输入、输出函数完成对结构体类型变量成员的输入(赋值)、输出。由于结构体类型变量成员的数据类型通常是不一样的,用scanf同时输入不同类型的成员数据常常会出现意想不到的情况,解决的方法有以下两种:,7.1结构体,利用转换函数将结构体类型变量成员以字符串的形式输入,利用C的类型转换函数将其转换为所需类型。类型转换的函数是:atoi(char*str);将数字字符串转换为整型。atof(char*str);将数字字符串转换为双精度的实型。atol(char*str);将数字字串转换长整型。使用上述函数,要包含头文件stdlib.h。,7.1结构体,例71类型转换函数在结构体变量数据输入中的应用示例。定义结构体类型及变量,输入一个学生的有关信息并输出。例71源程序,7.1结构体,利用简单变量 将结构体变量的成员的初始值用scanf()函数输入到简单变量中,再将其值赋给结构体成员变量。例72借助简单变量实现结构体变量的数据输入。例72源程序,7.2结构体数组的定义和引用,结构体数组的定义方法和基本型数组的定义方法相似,只需在定义数组时指明其数据类型为相应的结构体类型。结构体类型数组的定义形式为:struct 结构体名 数组名元素个数;例如:定义学生成绩结构体类型数组。struct stu_scr/*定义学生成绩结构体类型*/char name10;/*学生姓名*/char sex;/*性别*/float score3;/*三科考试成绩*/;struct stu_scr student20;/*定义具有20个元素的结构体数组student*/,7.2结构体数组的定义和引用,由于结构体数组中的每一个元素相当于一个结构体变量,因此引用其数组元素的成员的一般形式为:结构体数组名下标.成员名 对于上述结构体数组student,其数组元素各成员的引用形式为:student0.name、student0.sex、student0.scorei;student1.name、student1.sex、student1.scorei;.student19.name、student19.sex、student19.scorei;,7.2结构体数组的定义和引用,可以对结构体数组进行初始化,初始化方法与二维基本型数组的初始化方式相似。初始化的格式为:Struct 结构体名 数组n元素初值1,元素初值2,.,元素初值n;例如:对二维基本型数组以及结构体数组的初始化。int a24=1,2,3,4,5,6,7,8;struct stu_scr myclass2=zhang san,f,89,90,99,li si,m,89,95,78;,7.2结构体数组的定义和引用,结构体数组的输入与输出一般在循环结构中进行,一次循环可以输入或输出一条结构体记录。例73定义一个结构体数组用于存储和显示三个学生的基本信息。例73源程序程序运行结果如下:,no.name sex age depart06030217 zhang san m 19 Economy&Commerce06050105 li si m 18 engineering06010116 wang wu f 18 Computer science,7.3指向结构体的指针,指向结构体变量的指针变量的定义与指向基本类型的指针变量的定义一样。其一般定义形式为:Struct 结构体类型名*指针变量名 例如:定义指向结构体变量的指针变量。struct stu_info*q;/*q为指针变量,指向结构体类型stu_info*/例如:在定义结构体类型时定义结构体指针变量。,7.3指向结构体的指针,struct stu_cj char no8;/*学号*/char name10;/*姓名*/float score4;/*3门课成绩及平均成绩*/stud,*p1,*p2;/*p1、p2为指针变量,指向结构体类型stu_cj*/系统为结构体变量分配存储空间后,结构体变量及成员都有自己的地址。例如:变量的地址:&stud 成员的地址:&stud.no、&stud.name、&stud.score0,7.3指向结构体的指针,将一个结构体变量的起始地址赋给一个指针变量后,则指针变量就指向该结构体变量,可以通过该指针变量来引用结构体变量的成员,引用形式为:指针变量-成员;(*指针变量).成员;例如:指针变量p1,p2指向结构体变量x。p1=p2=例如:通过结构体指针p1和p2来引用结构体变量x成员。以下三种方式是等价的。x.no、x.name、x.score0p1-no、p1-name、p1-score0(*p2).no、(*p2).name、(*p2).score0,7.3指向结构体的指针,将结构体数组的起始地址赋予指针变量,则指针变量指向结构体数组,可以通过该指针变量访问数组元素。例74使用指向结构体数组的指针来访问结构体数组例7.4源程序,7.4结构体与函数,结构体传递给函数的方式 向函数传递结构体变量的成员 向函数传递结构体变量 传递结构体变量的地址,7.4结构体与函数,结构体作为函数形参,函数的定义形式:传值方式:返回类型 函数名(struct 结构体名 形参名,)传引用方式:返回类型 函数名(struct 结构体名*形参名,)结构体作为函数实参,函数的调用形式:传值方式:函数名(结构体变量名,)传引用方式:函数名(结构体变量名,),7.4结构体与函数,例 75 用函数实现结构体类型数据的输入和输出。例 7.5源程序例 7.6 根据表7-2所示学生成绩表,定义相应的结构体类型,编程求解每门课的平均成绩及每个学生的平均成绩。,7.4结构体与函数,表 72 学生成绩表,例 7.6源程序,7.5复杂的结构体,结构体成员可以是基本类型,也可以是其他构造类型的成员。结构体中还可以包含其他结构体。例如:结构体类型std_info中包含另一结构体类型date。struct date/*日期结构类型:由年、月、日三项组成*/int year;int month;int day;,7.5复杂的结构体,struct std_info/*学生信息结构类型:包括学号、姓名、性别和生日*/char no8;char name10;char sex;struct date birthday;,7.5复杂的结构体,例如:结构体类型std_record中包含结构体类型std_info和score。struct score/*成绩结构类型:由学号和三门成绩共4项组成*/char no8;int score1;int score2;int score3;student std_record struct std_info stdxx;struct score cj;,7.5复杂的结构体,如果某成员本身又是一个结构类型,则只能通过逐级使用成员运算符引用结构体成员。对最低一级的成员进行引用格式为:结构变量.成员.子成员.最低级子成员,7.5复杂的结构体,例77多级结构体成员的引用。例77源程序,7.5复杂的结构体,在结构体定义时,其成员定义为自身的结构体类型,这种方式称为结构体的自我引用。例如:结构体中包含指向自身的结构体指针。struct linkchar m;struct link*next;/*定义指向自身的结构体指针*/;struct link x,y,z;/*结构体变量的定义*/,7.6链表,链表结构 单链表结构如图所示。链表的基本操作有:创建、检索(查找)、插入、删除和修改等。,7.6链表,在C语言中,用结构体类型来描述链表结点结构。例如:struct link int data;/*数据域*/struct link*next;/*指针域*/;结构体类型中具有一个指向自身结构体类型的指针域的链表称为单链表或线性链表,有两个指针域的链表称为双向链表或二叉链表等,有三个及以上指针域的链表称多重链表。,7.6链表,链表是一种动态存贮结构。在C语言中,使用系统提供的动态内存管理函数,为链表中的结点分配存储空间,或释放链表中的结点占用的存储空间,以实现数据的动态存储。内存分配函数主要有malloc()和calloc(),内存释放函数是free()。这两类函数的原型在stdlib.h中,要使用这些函数时,首先要用文件包含:include“stdlib.h”或include,7.6链表,内存分配函数malloc()函数原型:void*malloc(int size);功能:分配大小为size的内存空间,返回分配内存空间的起始地址(void*类型)。若未能实现内存分配,则返回空指针(NULL)。例如:int*p,*q;p=(int*)malloc(sizeof(int);/*分配2B,即整型数据长度*/q=(int*)malloc(sizeof(int)*100);/*分配200B*/,7.6链表,内存块分配函数calloc()函数原型:void*calloc(unsigned n,unsigned size);功能:分配n块大小为size的内存空间,返回该内存区域的起始地址。若未能实现分配,则返回空指针(NULL)。例如:int*p;p=(int*)calloc(10,2);/*分配10块大小为2B内存空间*/,7.6链表,内存释放函数 free()函数原型:void free(void*block);功能:释放block指向的内存区域。block应当为内存分配函数的返回值。例如:int*p;p=(int*)malloc(4);/*分配空间*/*p=100;free(p);/*释放 p 所指的内存空间*/p=(int*)malloc(sizeof(int)*100);free(p);,7.6链表,动态链表是指在程序执行过程中动态分配链表的结点空间而建立的长度可变的链表。创建动态链表有两种方式,一种是首部插入结点方式,一种是尾部插入结点方式。首部插入结点方式:新生成的结点插入在表头,最后插入的结点即为表头结点。尾部插入结点方式:新生成的结点插入到链表的最后。,7.6链表,例78编写一个create()函数,创建一个动态单链表,即链表中的结点个数不限。算法思路:创建一个空链表;生成一个新结点:向系统申请分配一个结点的空间,然后输入结点数据域的数据项,并将指针域置为空(链尾标志);将新结点插入到链表尾部。对于链表的第一个结点,应设置头指针变量。例78源程序,7.6链表,链表的插入操作是在结点ai-1与ai之间插入一个新的结点ax,使线性表的长度增1。,单个结点的后插操作,建立新结点 s=(struct node*)malloc(sizeof(struct node),改变新结点前一结点的指针 p-next=s;,向新结点中添入内容,将新结点链入链中 s-next=p-next;,7.6链表,例79编写一个insert()函数,实现在单链表的第k个结点后插入1个新结点的操作。当k=0时,将新结点插入到第一个结点之前,成为链表新的首结点。当k大于链表长度,则将新结点插入到链表最后。例79源程序,7.6链表,删除操作是将链表的第i个结点删去。因为在单链表中结点ai的存储地址是在其直接前趋结点ai-1的指针域next中,所以应首先找到ai-1的存储位置p。然后令pnext指向ai的直接后继结点,即把ai从链上摘下。最后释放结点ai的空间。,链表的删除操作的实现步骤:1.找直接前驱结点;2.从链表中删除结点;3.释放空间。,删除后继结点,7.6链表,例710编写一函数,实现链表的删除操作。例710源程序,7.6链表,链表的查找操作是根据检索条件,顺序查找某个结点是否存在于链表中,若存在,则返回结点的存储位置,否则返回空。链表的查找分按序号查找和按值查找两种方式。,7.6链表,按序号查找由于链表是一个顺序访问结构,因些,即使知道被访问结点的序号i,也不能直接按序号i访问结点,而只能从链表的头指针出发,按指针域next逐个结点往下搜索,直到搜索到第i个结点为止。例711编写一函数,实现链表的按序号查找。例711源程序,7.6链表,按值查找按值查找是在链表中查找是否有结点值等于给定值key的结点,如果有的话,则返回首次找到的其值为key的结点的存储位置;否则返回NULL。查找过程从头指针出发,顺着链表逐个将结点的值和给定值key作比较。例712编写一函数,实现链表的按值查找。例712源程序,7.6链表,链表的输出操作从链表的头指针开始依次输出各结点的数据域。例713编写一函数,实现链表的输出。例713源程序,7.6链表,例714链表操作综合示例。利用create()建立一个链表,并实现链表的插入、删除、查找和输出等操作。例714源程序,7.7共用体,共用体类型的定义与结构类型的定义类似。union 共用体类型名数据类型 成员1;数据类型 成员2;数据类型 成员n;例如:union dataint ii;char cc;float ff;;,7.7共用体,共用体变量的定义与结构体变量的定义类似,也有三种定义方式。先定义类型、再定义变量例如:定义共用体类型data变量。union data un1,un2,un3;/*un1、un2和un3为共体体类型变量。*/,7.7共用体,定义类型时定义变量在定义共用体类型时,将变量定义写在“”与“;”之间。例如:union data int ii;char cc;float ff;u4,u5;/*un4和un5为共用体类型变量。*/,7.7共用体,直接定义共用体类型的变量例如:缺省共用体类型名,直接定义变量unionint i;char ch;float f;un7,un8;,7.7共用体,共用体与结构体都是由成员构成,但二者有本质的不同。结构体中各成员有自身的独立的存储空间,结构体变量的长度为各成员长度之和,而共用体变量所占用的内存空间等于最长成员的长度,而不是各成员长度之和,各成员在共同体变量存储空间内享有自身类型大小的存储空间,成员间的存储空间有重叠。,7.7共用体,例如:结构体变量与共用体变量存储的区别。结构体:共用体:struct st_data union un_data;int ii;int ii;char ch;char ch;float ff;float ff;st_var;un_var;,7.7共用体,7.7共用体,共用体变量成员引用共用体变量与结构体变量一样,也只能逐个引用共用变量的成员。共用体变量成员引用三种形式:共用体变量名.成员名 指针变量名-成员名(*指针变量名).成员名 例如:访问共用变量un1各成员:un1.i、un1.ch、un1.f。,7.7共用体,例如:union un_data1 int i;float f;char str5;undata,*unp;unp=undata.str、undata.str1。unp-str、unp-str1。(unp).str、(unp).str1,7.7共用体,共用体变量定义时也可以初始化,但初始化值只能给第一个成员。例715共用体变量的初始化 例715源程序程序运行结果是:x:65 A 0.000000 y:70 F 0.000000y=x 65 A 0.000000,7.7共用体,例716将一个16位(2B)整型数据按高8位(1B)和低8位(1B)输出。例716源程序程序运行结果为:ax=1234 ah=12 al=34,7.8位域,位域的定义方式与一般结构体成员相似。在结构体类型定义时,只需定义相应的以位为单位的成员即可。位域定义的一般形式为:struct 结构体类型名类型说明符 位域名1:位域长度1;类型说明符 位域名2:位域长度2;类型说明符 位域名n:领域长度n;,7.8位域,例如:定义包含位域的结构体类型及变量。struct st_bs1unsigned a:7;unsigned b:2;char c;unsigned d:4;struct st_bs1 b_data;/*定义变量*/,7.8位域,位域的引用方法与结构体成员的引用方法相同,使用成员运算符“.”来引用结构体变量的位域。引用的一般形式为:结构体变量名.位域名 例7.17位域的引用示例。例7.17源程序程序运行结果为:1,7,151,7,15,7.9枚举类型,枚举类型的定义形式:enum 枚举类型名 枚举取值列表;例如:enum weekdays sun,mon,tue,wed,thu,fri,sat;说明:枚举取值列表中应列出该类型的变量所用可能的取值,这些值也称为枚举元素,是C语言用以描述客观事物的合法标识符。,7.9枚举类型,枚举变量的定义与结构体变量和共用体变量定义类似。间接定义方式:先定义类型,再定义变量。例如:enum weekdays sun,mon,tue,wed,thu,fri,sat;/*先定义类型*/enum weekdays workday;/*再定义变量*/直接定义方式:在定义类型时定义变量。例如:enum weekdays sun,mon,tue,wed,thu,fri,sat workday;/*定义类型时定义变量*/,7.9枚举类型,例718定义一个描述三种颜色的枚举类型red、blue、green,输出这三种颜色的全部排列结果。例 718源程序,7.10类型定义typedef,Typedef的使用格式为:typedef 数据类型名 标识符;例如:typedef int INTEGER;为int类型一个新的类型名INTEGER。则有:INTEGER i,j,a10;等价于:int i,j,a10;,7.11小结,结构体是C语言中的一种构造数据类型,用于存储和处理不同类型的一体化的数据。在处理实际问题时,客观事物的描述往往需要多种类型的数据,采用结构体,通过成员引用可以灵活、方便地处理这些不同类型的数据。结构体类型必须由用户定义,结构体类型变量的定义有三种方式:先定义类型再定义变量;定义类型时定义变量;缺省类型名,直接定义变量。,7.11小结,结构体变量的成员用成员运算符引用,引用有三种方式:结构体变量名.成员名;指向结构体的指针变量-成员名;(*指向结构体的指针变量).成员名结构体变量中各成员有自身独立的存储空间,结构体变量的长度为各成员长度之和。结构体变量可以作为函数参数,函数也可返回结构体类型的数据。,7.11小结,数组可以作为结构体成员,结构体也可构成结构体数组,结构体数组广泛用于一批数据元素的处理。指针可以作为结构体中的成员,也可能指向结构体。结构体变量与其成员都有地址,指向结构体的指针可以引用结构体成员。应用结构体容易实现数据结构中最为常用的一种数据结构链表,7.11小结,共用体也称为联合体,是C语言中的一种构造数据类型,该类型的成员大小可能不同,但共享一块起始位置相同的存储区,以节省存贮空间。共用体类型定义及变量定义方式与结构体相似。共用体变量所占用的内存空间等于最长成员的长度,各成员在共用体变量存储空间内在享有自身类型大小的存储空间,成员间的存储空间有重叠。共用体中可以有结构体,结构体中也可以有共用体。共用体的实际应用通常都是与结构体相互结合在一起的。,7.11小结,枚举类型是C语言提供的一种数据类型,它将变量可能的值一一列举出来,其作用主要是使代码更具可读性和提高程序的运行效率。枚举类型变量的定义方式与结构体和共用体相似。枚举元素的取值是根据定义时的顺序,第一个元素取0,后一个元素是前一个元素的值增1。枚举类型定义时也可人为改变枚举元素的值。枚举类型变量只有赋值运算和关系运算。,