1、1,第十一章 结构体与共用体,概述,定义结构体类型变量的方法,结构体变量的引用,结构体变量的初始化,结构体数组,指向结构体类型数据的指针,共用体,用typedef定义类型,2,问题的提出:,首先回顾我们经历过例题,就是有关学生等信息的排序及查找的问题。,11.1 概述,例:新生入学登记表,要记录每个学生的学号,姓名,性别,年龄,身份证号,家庭住址,家庭联系电话等信息。,2,3,在处理此问题时,要定义七个数组分别存放学生的信息。,如果还要增加有关学生的其它信息如平时成绩、总成绩、学分等,则均要设置相应的数组存放这些信息。 这些信息的类型不一定相同,但它们之间是有关系的。如果用一个个独立的数组存放
2、,则很难看出它们之间的关系,能否将一个学生的信息作为一个完整的类型存放呢?,3,4,为了能方便地处理此类问题,在高级语言中,规定了一种新的数据类型,因为这种类型是由用户自己确定的,故称为用户自定义的类型,在C中,这种类型被称为结构体。,在一个名字下的一组变量,而这组变量的类型可以不同,在PASCAL、FORTRAN 90等语言中称为记录类型,在Visual BASIC中称为用户自定义类型。,4,5,结构体 结构体是一种构造数据类型 用途:把不同类型的数据组合成一个整体-自定义数据类型结构体类型定义,struct 结构体名 类型标识符 成员名;类型标识符 成员名;. ;,成员类型可以是 基本型或
3、构造型,struct是关键字, 不能省略,合法标识符 可省:无名结构体,注意“;” 不能省略,5,6,例 struct student int num;char name20;char sex;int age;float score;char addr30;,结构体类型定义描述结构 的组织形式,不分配内存,结构体类型定义的作用域,6,7,例 #define STUDENT struct studentSTUDENT int num;char name20;char sex;int age;float score;char addr30; STUDENT stu1,stu2;,struct 结构
4、体名 类型标识符 成员名;类型标识符 成员名;. ; struct 结构体名 变量名表列;,11.2 结构体变量的定义 1、先声明结构体类型,再定义结构体变量 一般形式:,7,8,2、在声明类型的同时定义结构体变量 一般形式:,struct 结构体名 类型标识符 成员名;类型标识符 成员名;. 变量名表列;,8,9,3、直接定义结构体变量 一般形式:,struct 类型标识符 成员名;类型标识符 成员名;. 变量名表列;,例 struct int num;char name20;char sex;int age;float score;char addr30;stu1,stu2;,用无名结构体
5、直接定义 变量只能一次,9,10,说明: 结构体类型与结构体变量概念不同 类型:不分配内存; 变量:分配内存 类型:不能赋值、存取、运算; 变量:可以 结构体可嵌套,10,11,结构体成员名与程序中变量名可相同,不会混淆,例 struct student int num;char name20;float score;char addr30;stu1,stu2; main() int num;float score; ,11,12,11.3 结构体变量的引用 引用规则结构体变量不能整体引用,只能引用变量成员,成员(分量)运算符 优先级: 1 结合性:从左向右,引用方式: 结构体变量名.成员名,
6、12,13,可以将一个结构体变量赋值给另一个结构体变量结构体嵌套时逐级引用,13,14,11.4 结构体变量的初始化形式一:,struct 结构体名 类型标识符 成员名;类型标识符 成员名;. ; struct 结构体名 结构体变量=初始数据;,例 struct student int num;char name20;char sex;char addr30; struct student stu1=112,“Wang Lin”,M, “200 Beijing Road”;,14,15,形式二:,struct 结构体名 类型标识符 成员名;类型标识符 成员名;. 结构体变量=初始数据;,例 s
7、truct student int num;char name20;char sex;char addr30;stu1=112,“Wang Lin”,M, “200 Beijing Road”;,15,16,11.5 结构体数组 11.5.1 定义结构体数组 定义:数组中每个元素都是一个结构体类型的数据。 三种形式:,形式一: struct student int num;char name20;char sex;int age; struct student stu2;,形式二:struct student int num;char name20;char sex;int age;stu2;
8、,形式三:struct int num;char name20;char sex;int age;stu2;,16,17,11.5.2 结构体数组初始化,顺序初始化:struct student int num;char name20;char sex;int age; struct student stu =100,“Wang Lin”,M,20,101,“Li Gang”,M,19,110,“Liu Yan”,F,19;,例 struct student int num;char name20;char sex;int age;stu = ;,例 struct int num;char n
9、ame20;char sex;int age;stu =,;,17,18,结构体数组引用,引用方式: 结构体数组名下标.成员名,18,19,【例11.2】对候选人得票的统计程序,设有三个候选人,每次输入一个得票的候选人的名字,要求最后输出各人得票结果.,11.5.3 结构体数组应用举例,分析: 定义一个结构体类型person含两个成员name和count 定义一个结构体数组leader它有三个元素 键盘输入候选人名字与结构体中leaderj.name比较 判断后leader.count+.,19,20,#include struct person char name20;int count;
10、leader3=“Li”,0,“Zhang”,0,”Fun”,0; main() int i,j; char leader_name20;for(i=1;i=10;i+) scanf(“%s“,leader_name);for(j=0;j3;j+)if(strcmp(leader_name,leaderj.name)=0)leaderj.count+;printf(“n”);for(i=0;i3;i+) printf(“%5s:%dn“,leaderi.name,leaderi.count); ,运行情况如下:LiLiFuZhang ZhangFuLiFuZhangLiLi:Zhang:Fu:
11、,20,21,11.6 指向结构体类型数据的指针 11.6.1 指向结构体变量的指针 定义形式:struct 结构体名 * 结构体指针名; 例 struct student *p;,使用结构体指针变量引用成员形式,存放结构体变量在内存的起始地址,指向运算符 优先级: 1 结合方向:从左向右,例 :指向结构体的指针变量,21,#include “string.h” main() struct student long int num;char name20;char sex;float score;stu_1,*p;p= ,22,试分析一下几种运算符:,p-n 得到指向的结构体变量中的成员n的值
12、。,p-n+ 得到指向的结构体变量中的成员n的值,用完该值后使它加 1。,+p-n 得到指向的结构体变量中的成员n的值加 1,然后再使用它。,22,23,11.6.2 指向结构体数组的指针,指针变量也可以用来指向结构体数组中的元素,【例11.4】 指向结构体数组的指针,struct student int num;char name20;char sex;int age; stu3=10101,“Li Lin“,M,18,10102,“Zhang Fen“,M,19,10104,“Wang Min“,F,20; main() struct student *p;printf(“No. Name
13、 Sex Agen”);for(p=stu;pnum,p-name,p-sex,p-age); ,23,24,(1) 上述运算符具有相同的运算优先级和结合性; (2) +pnum的执行效果等价于: +(pnum); (3) (+ +p)num的执行效果是:先执行p=p+1,然后再执行pnum(注意p的值已经改变); (4)(p+ +)num的执行效果是:先存取pnum,然后再执行p=p+1;,对“”、“”、 “()”的进一步说明:,24,25,11.6.3 用结构体变量和指向结构体的指针作函数参数 用结构体变量的成员作参数-值传递 用指向结构体变量或数组的指针作参数-地址传递 用结构体变量作参
14、数-多值传递,效率低,(X),【例11.5】有一个结构体变量stu,内含学生学号、姓名和三门课的成绩,要求在main函数中赋以值,在另一函数print中将它们打印输出。,25,26,#include “string.h“ #define FORMAT “%dnsfffn” struct student int num;char name20;float score3; main() void print(struct student);struct student stu;stu.num=12345;strcpy(stu.name,”LiLi”);stu.score067.5stu.score
15、189;stu.score2=78.6;print(stu);,void print(struct student stu) printf(FORMAT,stu.num,stu.name, stu.score0,stu.score1,stu.score2);printf(”n“); ,27,【例11.6】将上题改用指向结构体变量的指针作实参。 #include “string.h“ #define format “%dnsfffn” struct studentint num;char name20;float score3;stu=12345,”LiLi”,67.5,89.78,78.6;
16、main() void print(struct student *); /*形参类型修改成指向结构体的指针变量*/print(&stu); /*实参改为stu的起始地址*/void print( struct student *) /*形参类型改了*/ printf(FORMAT,p-num,p-name,p-score0,p-score1,p-score2);printf(”n“);,28,11.7 用指针处理链表,链表是一种常见的重要的数据结构。它是动态地进行存储分配的一种结构。,数组必须事先定义固定的长度(即元素个数)。比如,有的班级有人,而有的班只有人,如果要用同一个数组先后存放不同
17、班级的学生数据,则必须定义长度为100的数组。如果事先难以确定一个班的最多人数,则必须把数组定得足够大,以能存放任何班级的学生数据。显然这将会浪费内存。链表则没有这种缺点,它根据需要开辟内存单元。,29,一、链表的概念,链表就是一组组的数据用链将它连起来,如下图所示。,数据1,数据2,数据5,数据3,数据4,数据,链,问题是用什么来存放数据,用什么来作为链。解决办法:,29,30,数据域,指针域,从上图知,可以定义一个结构,其中一部分存放数据,另一部分存放指向下一数据的指针。这就构成了单链表。,如果增加指向上一数据的指针,则就构成了双链表。,节点,每组数据和指针称为链表的一个节点。,30,31
18、,数据域,指针域,节点,双链表有两个指针,一个指向上一个数据,一个指向下一个数据。,单链表只能从表头开始访问节点元素,或者从当前节点访问它以后的节点。双链表可以从表中的任何一个节点开始访问其它的节点。,31,32,struct node type data symbol;struct node *first;struct node *next; ,二、链表中结构的定义方式,数据域,指向上一个节点的指针,指向下一个节点的指针,从上面的定义可看出,链表中结构的定义是递归的。,对于链表的操作,都是通过指向链表节点的指针进行的。因为每次从系统申请到的内存,得到是指向该内存的指针。,32,33,例: 用
19、链表处理文本编辑程序。每次输入若干行文本,然后输出。,设计思路:1 从内存中申请到一个节点的空间,然后输入文本,将该节点放到链表中去;2 从系统申请下一个节点的空间,输入文本,将节点保存到链表中;3 当输入空行时,结束输入;4 找到链表的开始,从头将链表的内容输出。,33,34,申请第1个节点的空间,程序的运行过程分析,保存该节点指针到表头指针中,输入第1个节点的文本,说明变量,P,输入文本,pt,34,35,申请第1个节点的空间,程序的运行过程分析,保存该节点指针到表头指针中,输入第1个节点的文本,说明变量,P,输入文本,申请第2个节点空间,输入文本,将第1个节点中的指针指向第2个节点,P,
20、pth,pt,pth-p=pt,35,36,申请第1个节点的空间,程序的运行过程分析,保存该节点指针到表头指针中,输入第1个节点的文本,说明变量,P,输入文本,申请第2个节点空间,输入文本,将第1个节点中的指针指向第2个 节点,P,head,pth,申请第3个节点的空间,输入文本,将第2个节点中的指针指向第3个 节点,找到表头,逐个结构输出每行文本,P,pt,pth-p=pt,36,37,经过对程序运行过程的分析,可初步确定在设计程序时,有几个问题要注意:,1 要有3个指向结构的指针,一个用来保存表头,一个用来记录当前节点,一个用来记录当前节点前一个节点;,2 除第 1 个节点外,其它节点的过
21、程都一样。第 1 个节点要将指针保存到表头指针中。,3 找到前一个节点才能找到后一个节点,不能象数组那样可能找到中间的任何一个节点。,37,38,#include struct textchar txt81;struct text *p; ; struct text *head; main()int i,t=0;struct text *pt,*pth;pt=(struct text *)malloc(sizeof(struct text);gets(pt-txt);head=pth=pt;while(*(pt-txt) /*空行结束输入*/pt=(struct text *)malloc(s
22、izeof(struct text);gets(pt-txt); /* 输入一行文本*/pth-p=pt; /*将上一节点的指针指向当前节点*/pth=pt; /* 将当前节点的指针保存到 pth中*/,pt=head; /* 让指针指向链表的表头*/t=1;while(*(pt-txt)printf(“%d:“,t+);puts(pt-txt);pt=pt-p; /* 获取下一节点的指针*/ ,38,39,11.8 共用体 构造数据类型,也叫联合体 用途:使几个不同类型的变量共占一段内存(相互覆盖) 11.8.1共用体的概念 定义形式:,union 共用体名 类型标识符 成员名;类型标识符
23、成员名;. ;,例 union data int i;char ch;float f;,类型定义不分配内存,39,40,形式一:union data int i;char ch;float f;a,b;,形式二:union data int i;char ch;float f;union data a,b,c,*p,d3;,形式三:union int i;char ch;float f;a,b,c;,共用体变量的定义,共用体变量定义分配内存, 长度=最长成员所占字节数,共用体变量任何时刻 只有一个成员存在,40,41,11.8.2 共用体变量的引用方式 引用方式:,41,42,例 a.i=1;
24、a.ch=a;a.f=1.5;printf(“%d”,a.i); (编译通过,运行结果不对),共用体变量中起作用的成员是最后一次存放的成员,例 union int i;char ch;float f;a;a=1; (),不能在定义共用体变量时初始化,例 union int i;char ch;float f;a=1,a,1.5; (),可以用一个共用体变量为另一个变量赋值,例 main() float x; union int i; char ch; float f;a,b;a.i=1; a.ch=a; a.f=1.5;b=a; () x=a.f; ()printf(“%d,%c,%f,%fn
25、“,a.i,a.ch,a.f,x); ,引用规则 不能引用共用体变量,只能引用其成员,42,43,例:将一个整数按字节输出,运行结果: i=60501 ch0=101,ch1=141 ch0=A,ch1=a,main() union int_char int i;char ch2;x;x.i=24897;printf(“i=%on“,x.i);printf(“ch0=%o,ch1=%onch0=%c,ch1=%cn“,x.ch0,x.ch1,x.ch0,x.ch1); ,43,44,结构体与共用体 区别: 存储方式不同,联系: 两者可相互嵌套,44,45,11.9 枚举类型枚举类型:只能取事先
26、定义值的数据类型是枚举类型。,枚举类型定义,enum 枚举类型名枚举元素(或:枚举常量)列表;,枚举变量定义(类似结构体变量定义3种形式)定义枚举类型的同时定义变量:enum 枚举类型名枚举常量列表枚举变量列表;先定义类型后定义变量:enum 枚举类型名 枚举变量列表;匿名枚举类型:enum 枚举常量列表枚举变量列表;,如:enum weekday workday,week_end;workday和week_end被定义为枚举变量,它们的值只能是sun到sat之一。例如:workday=mon; week_end=sun; 是正确的。,45,46,当然,也可以直接定义枚举变量,如: enum
27、weekdaysun,mon,tue,wed,thu,fri,sat workday,week_end ;其中sun、mon、sat、等称为枚举元素或枚举常量。它们是用户定义的标识符。这些标识符并不自动地代表什么含义。例如,不因为写成sun,就自动代表“星期天“.不写sun而写成sunday也可以。用什么标识符代表什么含义,完全由程序员决定,并在程序中作相应处理。 说明.在编译中,对枚举元素按常量处理,故称枚举常量。它们不是变量,不能对它们赋值。例如sun=;mon; 是错误的。,47,.枚举元素作为常量,它们是有值的,C语言编译按定义时的顺序使它们的值为,。在上面定义中,sun的值为,mon
28、的值为1,sat为6。如果有赋值语句workdaymon; workday变量的值为1。这个整数是可以输出的。如printf(”d”,workday); 将输出整数。 也可以改变枚举元素的值,在定义时由程序员指定,如 enum weekday sun=7,mon=1,tue,wed,thu,fri,sat workday,week_end;定义sun为7,mon=1,以后顺序加1,sat为6.,48,3.枚举值可以用来作判断比较。如if(workday=mon)if(workdaysun) 枚举值的比较规则是:按其在定义时的顺序号比较。如果定义时未人为指定,则第一个枚举元素的值认作。故mon大
29、于sun,satfri。 一个整数不能直接赋给一个枚举变量。如workday; 是不对的。它们属于不同的类型。应先进行强制类型转换才能赋值。如workday(enum weekday); 它相当干将顺序号为的枚举元素赋给workday,相当于workdaytue; 甚至可以是表达式。如workday(enum weekday)(5-3);,49,【例11.13】口袋中有红、黄、蓝、白、黑五种颜色的球若干个。每次从 口袋中取出个球,问得到三种不同色的球的可能取法,打印出每种组 合的三种颜色。,球只能是种色之一,而且要判断各球是否同色,应该用枚举类型变量处理。设取出的球为I,j,k.根据题意,I,
30、j,k分别是种色球之一,并要求I!=j!=k.可以用枚举法,即一种可能一种可能地试,看哪一组符合条件。,算法可用图11.27表示。用n累计得到三种不同色球的次数。外循环使第一个球i从red变到black。中循环使第二个球j也从red变到black。如果i和j同色则不可取;只有i,j不同色(I!=j)时才需要继续找第三个球,此时第三个球也有种可能(red到black),但要求第三个球不能与第一个球或第二个球同色,即!=I,k!=j.满足此条件就得到三种不同色的球。输出这种三色组合方案。然后使n加。外循环全部执行完后,全部方案就已输出完了。最后输出总数n.,50,下面的问题是如何实现图1129中的
31、“输出一种取法”。 这里有一个问题:,如何输出“red”、“blue”、等单词。不能写成printf(“s“,red)来输出“red”字符串。可以采用图11.28的方法。为了输出个球的颜色,显然应经过三次循环,第一次输出i的颜色,第二次输出的颜色,第三次输出的颜色。在三次循环中先后将i,j,赋予pri。然后根据pri的值输出颜色信息。在第一次循环时,pri的值为1,如果的值为red,则输出字符串“red”,其它的类推。,程序(略),51,11.10 用typedef定义类型 功能:用自定义名字为已有数据类型命名 类型定义简单形式:typedef type name;,例 typedef int
32、 INTEGER;,类型定义语句关键字,已有数据类型名,用户定义的类型名,例 typedef float REAL;,类型定义后,与已有类型一样使用,例 INTEGER a,b,c; REAL f1,f2;,说明: 1.typedef 没有创造新数据类型 2.typedef 是定义类型,不能定义变量 3.typedef 与 define 不同,define typedef 预编译时处理 编译时处理 简单字符置换 为已有类型命名,51,52,typedef定义类型步骤 按定义变量方法先写出定义体 如 int i; 将变量名换成新类型名 如 int INTEGER; 最前面加typedef typ
33、edef int INTEGER; 用新类型名定义变量 如 INTEGER i,j;,例 定义结构体类型struct date int month;int day;int year;d;,例 定义结构体类型struct date int month;int day;int year;DATE;,例 定义结构体类型 typedef struct date int month;int day;int year;DATE;,类型定义可嵌套,52,53,课堂小结本章主要讲解结构体类型与结构体变量(数组)的定义、引用方法、初始化,及一种很有用的结构体数组,在使用中应根据实际情况,组织适当的结构,并定义相应的结构变量(数组),分别为其赋值。 注意:结构类型与结构变量的区别与联系。学习了结构变量与指针的应用,利用指针引用结构成员。,