1、C/C+程序设计程序设计 C/C+程序设计程序设计第第8章章 结构体与共用体结构体与共用体第第8章章 结构体、共用体结构体、共用体 引言引言 在生活及工程中,经常需要处理一些类型不同但密切关联的数据,这些数据通常共同用以描述一个事物的属性,一般关联使用而不宜拆分。由于其包含不同类型的数据,不能采用数组存放。在C语言中,提供了用户可自行定义的结构体类型,用以解决这种类型数据的存放和处理问题。8.1.1 结构体类型及结构体变量结构体类型及结构体变量1.结构体结构体类型类型 结构体类型声明一般格式:struct 结构体类型名结构体类型名 成员成员声明表声明表;说明:1)struct是关键字不能省略。
2、2)结构体类型名要符合C的标识符命名规则。3)成员声明表中各数据成员的定义方式同普通变量定义一样。4)结构体类型声明后的分号不可省略。例如,要描述一个学生的基本信息,包括学号(字符串)、姓名(字符串)、性别(字符串)、年龄(整型)。可定义如下结构体类型:struct stud char num8;/学号 char name20;/姓名 char sex3;/性别 int age;/年龄 ;numnamesexage 结构体类型的结构体类型的嵌套形式:一个嵌套形式:一个结构体成员的类型也可以是另一个结构体类型的变量。如:struct person中的成员birthday就是结构体类型struct
3、 date的一个变量。struct person char name21;char sex;struct date birthday;struct date int year;int month;int day;namesexbirthday yearmonthday8.1.1 结构体类型及结构体变量结构体类型及结构体变量2.结构体变量的定义结构体变量的定义 类型为结构体类型的变量为结构体变量。必须在声明一个结构体类型后,才可以用该类型定义结构体变量。结构体变量的定义有3种形式:1)在定义结构体类型结束后,再用该类型定义结构体变量)在定义结构体类型结束后,再用该类型定义结构体变量。例如:str
4、uct stud char num8,name20,sex3;int age;struct stud s1,s2;/函数外声明了结构体变量 s1、s2 说明:说明:1)结构体类型不占用空间,结构体变量占用内存空间。2)结构体变量各个成员在内存中顺序存放。其空间总和大于或等于各个成员所占存储空间的总和。(即:结构体变量所占的存储空间大小为结构体中最宽基本类型成员大小的整数倍,不足整数倍时就按内存对齐原则,凑足整数倍)。8.1.1 结构体类型及结构体变量结构体类型及结构体变量2)在定义结构体类型后直接定义变量。在定义结构体类型后直接定义变量。例如:struct stud char num8,nam
5、e20,sex3;int age;s1,s2;/类型后直接定义了结构体变量s1、s2 3)无名结构体,直接在其后定义变量)无名结构体,直接在其后定义变量。例如:struct char num8,name20,sex3;int age;s1,s2;/在无名结构体类型后直接定义变量s1、s2 说明:说明:无名结构体类型,只能在定义类型时进行一次性变量声明。8.1.1 结构体类型及结构体变量结构体类型及结构体变量【例例8-1】运行下列程序,查看运行结果。#include struct s1 double a;char ch;struct s2 int b;char ch;struct s3 shor
6、t int c;char ch;struct s4 char sno2;char ch;void main()printf(%4d,sizeof(struct s1);printf(%4d,sizeof(struct s2);printf(%4d,sizeof(struct s3);printf(%4dn,sizeof(struct s4);运行程序,输出:16 8 4 3试分析这个结果。试分析这个结果。8.1.1 结构体类型及结构体变量结构体类型及结构体变量完整程序完整程序3.结构体变量的初始化结构体变量的初始化由于结构体类型中各成员的数据类型不同,故结构体变量初始化其实是对各个成员变量分别
7、进行初始化。结构体变量初始化格式如下:struct 结构体类型名结构体类型名 成员列表成员列表;结构体变量名结构体变量名=各成员初值列表各成员初值列表;或者 struct 结构体类型名结构体类型名 成员列表成员列表;struct 结构体类型名结构体变量名结构体类型名结构体变量名=各成员初值列表各成员初值列表;说明:1)初值要用一对 括起来,各成员初值之间用逗号隔开。2)各成员的初值的次序与成员在类型定义中的次序一一对应且类型必须匹配。3)初值的个数少于等于超过成员数量。没有得到初值的成员,系统会自动按“清0”处理。8.1.1 结构体类型及结构体变量结构体类型及结构体变量例如:struct st
8、ud char num8,name20,sex3;int age;s=300001,Zhang,男,18 ;numnamesexage300001Zhang男男18有嵌套的结构体变量初始化:struct date int year,month,day;struct person char name20;char sex3;struct date birthdy;p1=杜鹏,男,1975,10,20 ;namesexBirthday yearmonthday 杜鹏男197510208.1.1 结构体类型及结构体变量结构体类型及结构体变量4.结构体变量的引用结构体变量的引用一个结构体变量中包含了若
9、干类型并不一定相同成员变量,因此不能直接对一个结构体变量整体进行输入、输出或者做一些其它操作(除赋值运算外),只能对结构体变量的各个成员变量分别进行操作。通过结构体变量名引用其成员的方法为:通过结构体变量名引用其成员的方法为:结构体变量名结构体变量名.成员名成员名其中,“.”是成员运算符,优先级最高。struct stud char num8,name20,sex3;int age;void main()struct stud s1,s2=300001,Zhang,男,18 ;对变量s2各成员的引用形式为:s2.num、s2.name、s2.age、s2.sex键盘输入s1各成员值:scanf
10、(%s%s%s%d,s1.num,s1.name,s1.sex,&s1.age);8.1.1 结构体类型及结构体变量结构体类型及结构体变量有嵌套声明的结构体变量成员的引用则需要通过多个成员运算符(“.”),一层层地找到最低一层的成员进行引用。struct date int year,month,day;struct person char name20;char sex3;struct date birthdy;p1=杜鹏,男,1975,10,20;p1的各成员引用形式为:p1.name、p1.ages1.birthday.yearp1.birthday.monthp1.birthday.da
11、y8.1.1 结构体类型及结构体变量结构体类型及结构体变量5.结构体变量的赋值结构体变量的赋值 通过赋值语句将一个结构体变量的值直接赋值给另一个同类型的结构体变量。除此之外,程序中不能以其它任何方式给一个结构体变量整体赋值。例如:若有语句“struct studs1,s2;”,则:1)s2=s1;是正确的。2)printf(“%s”,s1);是错误的。3)s2=“300002”,“liu”,“女”,19 ;是错误的。【例例8-2】编写程序,输入2个学生的个人信息,信息包括姓名(字符串)和年龄(整型),将2个学生信息互换后输出。分析:分析:先声明一个结构体类型(struct student),2
12、个学生信息需要定义两个结构体变量进行存放,而互换两个学生信息,还需要第三方结构体变量。注意,这3个结构体变量只有是同类型的,才可以相互间直接赋值。8.1.1 结构体类型及结构体变量结构体类型及结构体变量程序如下:#include struct student char name20;int age;void main()struct student s1,s2,temp;scanf(%s%d,s1.name,&s1.age);/输入第1个学生信息 scanf(%s%d,s2.name,&s2.age);/输入第2个学生信息 temp=s1;s1=s2;s2=temp;/此 3 条语句互换 2
13、个学生的信息 printf(%s%d n ,s1.name,s1.age);printf(%s%d t ,s2.name,s2.age);8.1.1 结构体类型及结构体变量结构体类型及结构体变量8.1.2 结构体数组结构体数组1.结构体数组的定义与初始化结构体数组的定义与初始化 类型为结构体的数组称为结构体数组结构体数组。结构体数组的声明格式:struct 结构体类型名 成员列表;数组名常量表达式;struct 类型名 成员列表;struct 类型名 数组名常量表达式;结构体数组初始化:例如:(用用 将每一个数组元素的初值括起来将每一个数组元素的初值括起来)struct stud char n
14、um8,name20,sex3;int age;s12=300001,Sun,男,18,300002,Li,女,19 ;struct stud s23=300001,Sun,男,18,300002,Li,女,19;注意:注意:也可以将所有初值用一对 括起来,这时要注意初值与元素对应关系以及与元素的各成员的对应次序。数组元素中没有得到初值的成员都会自动“清0”。2.结构体数组的使用结构体数组的使用结构体数组元素的成员的引用方法为:结构体数组名结构体数组名下标下标.成员名成员名【例例8-3】编写程序,从键盘输入3个学生信息并输出。程序如下:#include struct stud char num
15、8,name20,sex3;int age;void main()struct stud s3;int i;for(i=0;i 3;i+)printf(请输入第%d个学生的学号、姓名和年龄:n ,i+1);scanf(%s%s%s%d,s i.num,s i.name,s i.sex,&s i.age);续左边程序:printf(Num t Name t sex t age n );for(i=0;i num2)指针指向运算符(-):结构体指针名结构体指针名-成员名成员名 1)指针间接运算符(*)及成员运算符(.):(*结构体指针名结构体指针名).成员名成员名 如:(*p).num【例例8-4
16、】使用指向结构体变量的指针变量来访问结构体变量的各个成员。8.1.3 结构体指针变量及应用结构体指针变量及应用8.1.3 结构体指针变量及应用结构体指针变量及应用程序如下:#include struct stud char num8,name20,sex3;int age;void main()struct stud s=300001,Sun,男,18 ,*p=&s;printf(Num:%s t,p-num);printf(Name:%s t,p-name);printf(Sex:%s t,(*p).sex);printf(Age:%d n,(*p).age);4.结构体指针变量指向同类型的
17、结构体数组结构体指针变量指向同类型的结构体数组 一个结构体类型的指针变量可以指向同类型的结构体数组。通过该指针就可访问结构体数组元素。【例例8-5】使用指向结构体数组的指针变量来访问数组元素。输出3个学生信息。#include struct stud char num8,name20,sex3;int age;void main()struct stud s3=3001,Sun,男,18,3002,Li,女,19,3003,Zhao,男,20 ;struct stud *p=s;int i;printf(Num t Name t Sex t Age n );for(i=0;inum,p-nam
18、e,p-sex,p-age);8.1.3 结构体指针变量及应用结构体指针变量及应用8.1.3 结构体指针变量及应用结构体指针变量及应用 一个结构体变量作参数,仍属于按值传递方式。如果结构体一个结构体变量作参数,仍属于按值传递方式。如果结构体指针变量作参数,则属于按地址传递方式。指针变量作参数,则属于按地址传递方式。【例例8-6】阅读下列程序。#include struct stud char num8,name20,sex3;int age;void display_1(struct stud s)printf(%s t%s t%s t%dn ,s.num,s.name,s.sex,s.age
19、);void display_2(struct stud *p,int n)int i;printf(Num t Name t Sex t Age n );for(i=0;i name,(*p).sex,p-age);void main()struct stud s3=300001,Sun,男,18,300002,Li,女,19,300003,Zhao,男,20;int i;printf(Num t Name t Sex t Age n );for(i=0;i 3;i+)display_1(s i );printf(通过传递结构体数组,一次输出3个学生信息:n);display_2(s,3);
20、分析:分析:共用体(union)是指几个不同变量共同占用一块内存空间,它们拥有相同的内存地址。在C语言中,通过共用体类型实现这种内存覆盖技术。共用体也是一种用户自定义类型。与结构体(structure)类型很相似。它们都可以用来存储多种不同的数据类型,但是两者有着很大的区别,即共用体虽然能够存储不同的数据类型,但每一时刻只能存储其中的一个成员。8.2 共用体类型和枚举类型共用体类型和枚举类型8.2.1 共用体类型共用体类型1.共用体类型的定义共用体类型的定义 共用体类型的定义形式为:union 共用体类型名共用体类型名 成员成员列表列表;例如:union untp inti;char ch;f
21、loat f;1个字节成员ch的存储单元成员i,f的存储单元8.2.1 共用体类型共用体类型2.共用体类型变量的定义共用体类型变量的定义 共用体类型变量的定义与结构体类型变量的定义类似,也有三种方式:例如:1)先定义类型,再定义变量。unionuntp int i;char ch;float f;union untp un1,un2;3)无名共用体类型直接定义变量。例如:union int i;char ch;float f;un1,un2;/只可用一次 2)定义共用体类型时直接在其后定义变量。例如:union untp int i;char ch;float f;un1,un2;注意:注意:
22、共用体类型变量占用的存储空间大小等于占用空间最大的那个成员的字节数。例如,共用体变量un1、un2在内存中占用4字节。3.共用体类型变量的引用共用体类型变量的引用 共用体类型变量的引用与结构体类型变量一样,一般不能整体引用,只能引用它的某一个成员。例如,访问共用体类型变量un1各成员的格式为:un1.i、un1.ch、un1.f。4.共用体类型的主要特点共用体类型的主要特点1)各成员变量共享内存。在某一时刻存放并起作用的是最后一次存入的成员的值。2)共用体类型变量及其各成员的地址是相同的。3)不能对共用体类型变量进行初始化,但变量之间可以整体赋值。4)共用体类型可以出现在结构体类型定义中,反之
23、亦然。注意:使用共用体变量时要明白当前所起作用的成员是哪个,否则数据会没有意义。8.2.1 共用体类型共用体类型【例例8-7】阅读程序。void main()union un1 u1;struct stu1 s1;u1.a=E;u1.b=101;s1.c=H;s1.d=98;printf(a=%c,b=%d t ,u1.a,u1.b);printf(c=%c,d=%d n ,s1.c,s1.d);struct stu1 char c;int d;#include union un1 char a;int b;运行程序,输出:a=e,b=101 c=H,d=981.枚举类型的定义枚举类型的定义
24、所谓“枚举”是指将变量的值一一列举出来,变量的值只限于列举出来的值的范围内。枚举类型的定义格式:enum 枚举类型名枚举类型名 枚举元素值列表枚举元素值列表 ;例如:enum weekday sun,mon,tue,wed,thu,fri,sat ;说明:1)枚举类型适宜于表示有限的数据类型。如星期2)枚举元素的值是常量,不是变量,不能对枚举元素再进行赋值操作。3)枚举元素是有值的,按照定义时的顺序号,从0开始取值。例如,sun的值为0,mon的值为1,依此类推。枚举元素也可以比较大小,如sun mon8.2.2 枚举类型枚举类型4)枚举元素的值也可在定义时指定。例如,若有:enum week
25、day sun=7,mon=1,tue,wed,thu,fri,sat ;则sun的值为7,mon的值为1,从tue开始,枚举元素的值依次为2、3、4、5、6。再例如,若有:enum weekday sun=7,mon,tue,wed,thu,fri,sat ;则mon的值为8,tue的值为9,wed的值为10,依次类推。2.枚举类型变量的定义枚举类型变量的定义枚举类型变量的定义和结构体类型变量定义类似。有3种:如:1)enum color red,green,blue ;enum color c1;2)enum color red,green,blue c1;3)enum red,green
26、,blue c1;/只可使用一次 值得注意的是,一个枚举变量的值只能取相应枚举元素中的某一个,例如c1=red。8.2.2 枚举类型枚举类型【例8-8】枚举类型应用举例。程序如下:#include void main()enum weekday sun,mon,tue,wed,thu,fri,sat a,b,c;a=sun;b=wed;c=fri;printf(%d,%d,%d ,a,b,c);a=1;b=2;c=4;printf(n%d,%d,%d n,a,b,c);switch(a)case 0:printf(sunn);case 1:printf(monn);case 2:printf(
27、tuen);case 3:printf(wedn);case 4:printf(thun);case 5:printf(frin);case 6:printf(satn);运行结果:0,3,51,2,4mon8.2.2 枚举类型枚举类型8.3 使用使用typedef命名已有类型命名已有类型typedef命令 可以用一个别名别名来代替已有的某个类型名。定义的一般形式为:定义的一般形式为:typedef 已有类型名已有类型名 类型别名类型别名;如:typedef float MF;/指定 MF 为 float 类型的别名 MF x,y;/用别名MF定义了两个变量x、y,相当于:float x,y;
28、说明:说明:1)typedef语句只能够指定一个已有类型的别名,并不能生成或创建一种新的数据类型。2)用typedef为数组、结构体、共用体等类型定义别名,可使程序代码简洁。在数据结构等课程中用typedef定义类型别名的应用很多。8.4 单链表单链表结构体指针的应用结构体指针的应用8.4.1 单链表概述及动态内存分配单链表概述及动态内存分配1.单链表概念及其结点结构定义单链表概念及其结点结构定义 链表是结构体类型最重要的应用。链表可以实现内存的动态分配,提高内存的利用率。链表有单链表和双链表,其中单链表是最简单的一种链表。一个单链表由若干个结构相同的“结点”和一个指向头结点的“头指针”变量组
29、成。单链表的每个结点包含两部分:数据域和指针域。链尾结点的next为空(用NULL或符号表示)8.4 单链表单链表结构体指针的应用结构体指针的应用由于单链表的每个结点中存放了不同类型的数据,因此单链表的结点通常用结构体类型来描述.如:struct stu_node int num;char name10;int age;struct stu_node *next;2.单链表和数组的区别单链表和数组的区别1)单链表结点的存储空间是在程序运行过程中分配的,即“动态分配”,故单链表的结点在内存中地址通常是不连续的,结点之间通过指针域形成逻辑上的先后次序。因此,单链表的结点只能从前到后顺序访问,不能像
30、数组那样通过下标随机访问。2)数组元素的空间是连续的,空间分配是采用静态方式,程序运行中数组的内存空间不能改变,这种连续存储的方式使得数组适合采用随机访问方式。3.动态内存分配及释放动态内存分配及释放(1)malloc()函数 格式:格式:void *malloc(unsigned int size)功能:功能:动态申请一个长度为size字节的连续空间。申请成功,函数返回一个指向该空间的起始地址,申请失败,则返回空指针(NULL)。(2)calloc()函数 格式:格式:void *calloc(unsigned int n,unsigned int size)功能:功能:申请n个同一类型的大
31、小为size的连续内存空间,若成功,则返回连续内存空间的首地址,并对该空间清0,申请失败,则返回空指针(NULL)。8.4 单链表单链表结构体指针的应用结构体指针的应用(3)realloc()函数 格式:格式:void *realloc(void *p,unsigned int size)功能:将p指向的存储区的大小重新申请为size个字节。申请成功,返回首地址给p,不成功,则返回NULL给变量p。注意申请新空间不保证与原地址相同。使用该函数特别注意,若申请失败,则p原指向的内存被“遗失”了。从而导致程序崩溃。(4)free()函数 格式格式:void free(void *ptr)功能:释放
32、指针变量ptr所指向的内存区,其释放内存区的大小由ptr的类型决定。注意,ptr所指向的内存空间必须是由函数malloc()申请到的。8.4 单链表单链表结构体指针的应用结构体指针的应用8.4.2 单链表的主要操作单链表的主要操作有关单链表操作均使用下列类型定义:1.单链表的创建单链表的创建 从无到有地建立起一个有n个结点的单链表,即逐个申请结点空间、输入各结点数据并建立起前后结点之间的链接关系。创建步骤:1)定义结点的结构体类型。创建一个空链表。即head=NULL.2)利用malloc()函数申请一个结点空间。并置 next为空。若head仍为NULL,将head指向该结点。若head不空
33、,将该新结点接到表尾。3)判断结点数目是否已经满足,若不满足有则转2)继续,否则结束。struct node int data;struct node *next;8.4 单链表单链表结构体指针的应用结构体指针的应用8.4 单链表单链表结构体指针的应用结构体指针的应用struct node *creat(int n)struct node *head,*tail,*p;/head 为头指针,tail 为尾指针 int i=0,d;head=tail=NULL;while(i next=NULL;printf(input data:);scanf(%d,&d);p-data=d;if(head=
34、NULL)head=p;/新结点作为链表的第一个结点 else tail-next=p;/新结点链入原链表尾 tail=p;/新结点成为链表的最后一个结点 i+;/计数 return head;/链表第一个结点的地址作为函数的返回值 8.4 单链表单链表结构体指针的应用结构体指针的应用2.单链表的查询单链表的查询查询的方法就是从头到尾依次比较,直到找到该结点或者遇到NULL说明查找失败为止。查询并不会改变链表。int search(struct node *head,int x)struct node *p;p=head;while(p!=NULL)if(p-data=x)return 1;/
35、存在该结点 p=p-next;return 0;/查找失败,不存在该结点;8.4 单链表单链表结构体指针的应用结构体指针的应用3.单链表的插入操作单链表的插入操作插入的结点可以位于表头、表中或表尾。该操作可能修改链表。插入结点方法:(1)依次与表中结点相比较,找到插入位置。(2)修改指针,将该结点插入。将s插入到结点p之前的修改指针的顺序示意图如下:struct node*insert(struct node*head,int d)/将值为 d 的结点插入到单链表,保持 data 升序 struct node *p,*q,*s;s=(struct node*)malloc(sizeof(str
36、uct node);s-data=d;if(head=NULL)|(s-data data)s-next=head;/在表头插入 head=s;else p=head;/查找单链表中 data 的值比 d 大的第一个结点 while(p!=NULL&p-data data)q=p;p=p-next;s-next=p;/插入结点的第 1 步 q-next=s;/插入结点的第 2 步,必须按这个顺序修改指针 return head;8.4 单链表单链表结构体指针的应用结构体指针的应用4.单链表的删除操作单链表的删除操作删除的结点可以在表头、表中或表尾。会对链表进行修改struct node *delete(struct node *head,int d)/删除 值为 d 的结点 struct node *p,*q;p=head;q=p;while(p!=NULL&p-data!=d)q=p;p=p-next;if(p=NULL)printf(无此结点n);else if(p=head)head=p-next;else q-next=p-next;free(p);/释放 p 所指向的结点 return head;