520 likes | 731 Views
第九章 结构体与共用体. 【 教学要求 】 1 .理解结构体的含义。 2 .掌握结构体类型变量的定义方法。 3 .掌握结构体类型变量的引用方法。 4 .掌握结构体类型变量如何在定义的同时初始化。 5 .理解共同体的含义,掌握共同体类型变量的定义方法。 6. 了解结构体数组的定义和数组元素的引用。 7 .了解指向结构体类型数据的指针的概念及使用。 8 .了解链表结点的结构形式,链表的基本操作。 9 .了解枚举类型的定义,及枚举类型的输入输出。 10 .了解 TYPEDEF 的作用。. 第九章 结构体与共用体. 结构体 ( struct )
E N D
第九章 结构体与共用体 【教学要求】 1.理解结构体的含义。 2.掌握结构体类型变量的定义方法。 3.掌握结构体类型变量的引用方法。 4.掌握结构体类型变量如何在定义的同时初始化。 5.理解共同体的含义,掌握共同体类型变量的定义方法。 6. 了解结构体数组的定义和数组元素的引用。 7.了解指向结构体类型数据的指针的概念及使用。 8.了解链表结点的结构形式,链表的基本操作。 9.了解枚举类型的定义,及枚举类型的输入输出。 10.了解TYPEDEF的作用。
第九章 结构体与共用体 结构体(struct) 共同体(union) 结构体与共用体小结 枚举类型(enum) typedef定义类型
结构体(struct) 结构体的概念 结构体数组 结构体指针
结构体的概念 结构体的定义 结构体变量的存储特点 结构体变量的引用 结构体的初始化
结构体的定义 1、含义:不同类型数据的集合。 2、功能:用于描述一个“概念”。(或记录) 如:num name sex age score addr 10010 LiFun M 18 87.5 BeiJing 3、定义方法: 方法一:在定义结构体类型的同时,直接给出结构体变量。 如: struct 结构体名 { 结构体成员变量的定义; } 变量名1,变量名2,...,变量名n; 方法二:先给出结构体类型的定义,再定义结构体变量。 如: struct 结构体名 { 结构体成员变量的定义; 例: } ; struct 结构体名 变量名1,变量名2,...,变量名n; 4、要点:
方法一: struct student { int number; char name[20]; char sex; int age; float score; char addr[30]; }student1,student2; 请注意两种方法的不同特点: 1、“;”的用法; 2、方法二可以用一个*.h文件来存储结构体的定义。 方法二: struct student { int number; char name[20]; char sex; int age; float score; char addr[30]; }; struct student student1,student2;
struct date {int month,day,year;}; struct student { int num; char name[20]; char sex; int age; struct date birthday; char addr[30]; }student1,student2; 4、要点: 结构体名(struct): 用于标识一种新的数据类型, 即结构体类型。 注意区分结构体类型与基本数 据类型的不同,它是复合数据 类型。 结构体成员变量与普通变量的 定义一样,它还可以是结构体 变量。
student1.number student1.name[0] ... student1.name[4] student1.sex student1.age student1.score student1.addr[0] ... student1.addr[9] student2.number student2.name[0] ... student2.name[4] student2.sex ... student1 student2 如: struct student { int number; char name[5]; char sex; int age; float score; char addr[10]; }student1,student2; 结构体变量的存储特点 struct student所占存储空间的大小为: 各成员变量所占存储单元字节数之和。
结构体类型变量的引用 一、原则:通过结构体的成员来引用结构体变量。 二、成员的引用方法为:结构体变量名.成员名 三、要点: 1.结构体变量不能整体输入输出,只能对它的成员进行操作. 如:student1.num; 2.如果成员本身又是一个结构体类型,则要使用若干个成员运算符,逐级找到最底层的成员. 如:student1.birthday.day 3.成员变量可以象一般的变量一样进行各种运算,只是在运算时要加上“.”运算符. 4.可以引用成员的地址.
结构体变量的初始化 一、可以给主函数中或外部存储类别和静态存储类别的结构体变量、数组赋初值。 1、对外部存储类型的初始化. 2、对静态存储类型的结构体变量进行初始化 二、给结构体变量赋初值不能跨越前面的成员而只给后面的成员变量赋值。
例exp9_1:对外部存储类型的初始化. struct student { long number; char name[20]; char sex; int age; float score; char addr[30]; }a={99641,”Li Ping”,’M’,21,67.0,”56 Tianjin Street”}; main() { printf(“No.:%ld\nname:%s\nsex:%c\naddress:%s\n”, a.numer,a.name,a.sex,a.addr); }
例exp9_2:对静态存储类型的结构体变量进行初始化.例exp9_2:对静态存储类型的结构体变量进行初始化. main(){ static struct student { long number; char name[20]; char sex; int age; float score; char addr[30]; }a={99641,”Li Ping”,’M’,21,67.0,”56 Tianjin Street”}; printf(“No.:%ld\nname:%s\nsex:%c\naddress:%s\n”, a.number,a.name,a.sex,a.addr); }
结构体数组 结构体数组的含义: 数组元素是结构体类型数据的数组称为结构体数组. 结构体数组的定义 结构体数组的初始化 结构体数组的应用举例
结构体数组的定义 一、含义:若干个相同的结构体类型变量组成的数据集合。 二、定义方法: struct 结构体名 { 例如: 结构体成员定义; }数组名[元素个数]; 三、结构体数组的使用方法 1、通过数组的下标(或指向数组的指针)来访问结构体变量。 2、再通过结构体变量的成员实现结构体数据的访问。 形式为:数组名[下标].成员名 如:stu[2].name=“Wang Ying”;
struct student { long number; char name[20]; char sex; int age; float score; char addr[30]; }stu[3]; 结构体数组的物理含义: 表示实体(或记录)的个数。 struct student { long number; char name[20]; char sex; int age; float score; char addr[30];}; struct student stu[3];
结构体数组的初始化 1、含义:在定义结构体数组时完成数组元素的赋值 2、方法:与一般数组元素赋初值的方法相同。 struct student { long number; char name[20]; char sex; int age; char addr[30]; }a[3]={{99641,”Li Ping”,’M’,21,”56 Tianjin Street”}, {99341,”Zhang Fan”,”F”,20,”78 Beijing Road”}, {99441,”Ren Zhong”,”M”,20,”34Shenyang Road”}};
例exp9_3: 对候选人得票的统计程序,设有三个候选人,每次输入一个候选人的名字,最后统计出每个候选人的得票的结果. struct person { char name[20];int count; }leader[3]={“Li”,0,”Zhang”,0,”Fan”,0}; main() {int i,j; char leader_name[20]; for(i=1;i<=10;i++) {scanf(“%s”,leader_name); for(j=0;j<3;j++) if(strcmp(leader_name,leader[j].name)==0) leader[j].count++;} printf(“\n”); for(i=0;i<3;i++) printf(“%5s:%d\n”,leader[i].name,lader[i].count);}
结构体指针 指向结构体变量的指针 指向结构体数组的指针 指向结构体的指针作函数的参数 *单链表
指向结构体变量的指针 一、含义: 1、结构体变量的指针: 指结构体变量所占内存单元的起始地址 2、指向结构体变量的指针 指向结构体变量的起始地址的指针变量. 二、定义方法与使用 三、要点: 1、必须将指针指向一个确定的结构体变量,如:p=&a; 2、通过结构体变量的指针访问结构体的方法为: a.num、(*p).num、p->num。((*p).num和*p.num不同) 3、区分下面的两种用法: p->n++ 和 ++p->n
1、结构体指针的定义: main(){ struct student { long number; char name[20]; char sex; int age; float score; char addr[30]; }a={99641,”Li Ping”,’M’,”56 Tianjin Street”},*p=&a; 2、结构体指针的使用: a.num,a.name,a.sex,a.addr (*p).num,(*p).name,(*p).sex,(*p).addr; p->num,p->name,p->sex,p->addr;
案例: 使用指向结构变量的指针来访问结构变量的各个成员。struct std_info /*学生信息结构类型:由学号、姓名、性别和生日共4项组成*/ {char no[7]; char name[9]; char sex[3]; struct date birthday; }; struct std_info student={“000102”,“张三”,“男”,{1980,9,20}};main() { struct std_info *p_std=&student; printf("No: %s\n", p_std->no); printf("Name: %s\n", p_std->name); printf("Sex: %s\n", p_std->sex); printf("Birthday: %d-%d-%d\n", p_std->birthday.year, p_std->birthday.month, p_std->birthday.day); }
通过指向结构变量的指针来访问结构变量的成员,与直接使用结构变量的效果一样。一般地说,如果指针变量pointer已指向结构变量var,则以下三种形式等价:通过指向结构变量的指针来访问结构变量的成员,与直接使用结构变量的效果一样。一般地说,如果指针变量pointer已指向结构变量var,则以下三种形式等价: (1)var.成员 (2)pointer->成员 (3)(*pointer).成员 /* “*pointer”外面的括号不能省!*/ 注意:在格式(1)中,分量运算符左侧的运算对象,只能是结构变量,;而在格式(2)中,指向运算符左侧的运算对象,只能是指向结构变量(或结构数组)的指针变量,否则都出错。 思考题:如果要求从键盘上输入结构变量student的各成员数据,如何修改程序?
指向结构体数组的指针 1、定义方法举例: 2、使用要点: 1.p++:是指针p指向数组的下个元素,而这个元素是由结构体类型数据组成的,它不是一个简单的变量. 2.(++p)->num:先使指针p指向结构体数组当前位置的下一个元素,再访问其元素成员num; 请区别与(p++)->num的不同。
例exp9_4:指向结构体数组的指针 struct student { long number;char name[20]; char sex;int age; char addr[30]; }a[3]={{99641,”Li Ping”,’M’,21,”56 Tianjin Street”}, {99341,”Zhang Fan”,’F’,20,”78 Beijing Road”}, {99441,”Ren Zhong”,’M’,20,”34 Shenyang Road”}}; main(){ struct student *p; for(p=a;p<a+3;p++) printf(“%ld,%-20s,%2c,%4d,%-20s\n”, p->num,p->name,p->sex,p->age, p->addr); }
指向结构体的指针作函数的参数 1、用结构体变量的成员作函数的参数与一般变量作函数的参数一致; 2、用指向结构体变量的指针作函数的参数,实参将地址传递给形参,与前面指针作函数参数一致; 3、新版本的c语言允许将整个结构体变量作为函数的参数进行传递,要求形参与实参的类型必须一致,而且程序占用内存大,运行速度慢. 4、举例
例exp9_5:结构体变量作为函数的参数 #include <string.h> #define FORMAT “%d\n%s\n%f\n%f\n%f\n” struct student {int num; char name[20]; float scaore[3]; }; main() {Void print(struct student stu); struct student stu; stu.num=12345, strcpy(syu.name,”Li li”); stu.score[0]=67.5; stu.score[1]=76.5; stu.score[2]=89; print(stu); } void print(struct student stu) {printf(FORMAT,stu.num, stu.name,stu.score[0], stu.score[1],stu.score[2]);}
void print(struct student *q) {printf(FORMAT,q->num, q->name,q->.score[0], q->.score[1],q->score[2]);} 例exp9_6:结构体变量的指针作为函数的参数 #include <string.h> #define FORMAT “%d\n%s\n%f\n%f\n%f\n” struct student {int num; char name[20]; float score[3]; }; main() {Void print(struct student *q); struct student stu,*p; stu.num=12345, strcpy(syu.name,”Li li”); stu.score[0]=67.5; stu.score[1]=76.5; stu.score[2]=89; p=stu; print(p);}
单链表 概述: 1.链表结构 链表作为一种常用的、能够实现动态存储分配的数据结构,在《数据结构》课程中有详细介绍。 (1)头指针变量head──指向链表的首结点。 (2)每个结点由2个域组成: 1)数据域──存储结点本身的信息。 2)指针域──指向后继结点的指针。 (3)尾结点的指针域置为“NULL(空)”,作为链表结束的标志。
2.对链表的基本操作 对链表的基本操作有:创建、检索(查找)、插入、删除和修改等。 (1)创建链表是指,从无到有地建立起一个链表,即往空链表中依次插入若干结点,并保持结点之间的前驱和后继关系。 (2)检索操作是指,按给定的结点索引号或检索条件,查找某个结点。如果找到指定的结点,则称为检索成功;否则,称为检索失败。 (3)插入操作是指,在结点ki-1与ki之间插入一个新的结点k’,使线性表的长度增1,且ki-1与ki的逻辑关系发生如下变化: 插入前,ki-1是ki的前驱,ki是ki-1的后继;插入后,新插入的结点k’成为ki-1的后继、ki的前驱. (4)删除操作是指,删除结点ki,使线性表的长度减1,且ki-1、ki和ki+1之间的逻辑关系发生如下变化:删除前,ki是ki+1的前驱、ki-1的后继;删除后,ki-1成为ki+1的前驱,ki+1成为ki-1的后继.
一、链表的含义: 当一个结构体中有一个成员是指向本结构体的指针时,通过这样的指针可以将若干个相同的结构体存储单元连接成一个新的数据结构。举例: 二、功能:可以根据需要动态的开辟存储空间。 1.malloc(size):在内存中动态的分配一个长度为size的连续空间; 2.calloc(n,size):在内存中分配n个长度为size的连续空间; 3.free(ptr):释放由ptr指针指向的内存区域. 二、链表操作 建立链表、在链表插入结点、删除链表结点
head num score NULL num score next num score next ... 单链表的结构:在C语言中,用结构类型来描述结点结构。 struct student { int num; float score; struct student *next; }; 结点 要点: 1、链表中的元素在内存中存放顺序是不连续的。 由next指针来连接各结点。 2、链表数据结构的实现,必须利用指针变量.
malloc() malloc() head num score NULL num score next num score next ... struct student *creat( ) {struct student *head,*p1,*p2; n=0;head=NULL; p1=p2=(struct student *)(malloc(sizeof(struct student))); scanf(“%ld,%f”,&p1->num,&p1->score); while(p1->num!=0) { n=n+1;if(n==1) head=p1; else p2->next=p1; p2=p1;p1=(struct student *)(malloc(sizeof(struct student))); scanf(“%ld,%f”,&p1->num,&p1->score);} p2->next=NULL;return(head);} 建立含n个节点的链表过程:
malloc() malloc() head num score next num score NULL num score next num score next malloc() 在链表中插入结点的过程 ...
struct student *insert(struct student *head,struct student *stud) { struct student *p0,*p1,*p2; p1=head; p0=stud; if (head==NULL) {head=p0;p0->next=NULL;} else { while (p0->num>p1->num) {p2=p1; p1=p1->next;} if (p0->num<=p1->num) { if (head==p1) head=p0; else p2->next=p0; p0->next=p1;} else {p1->next=p0;p0->next=NULL;}} n=n+1; return(head);}
head num score next num score next num score next ... malloc() 删除链表结点的过程:
struct student *del(struct student *head,long num) {struct student *p1,*p2; if (head==NULL) printf("list is null!\n"); else {p1=head; while (num!=p1->num&&p1->next!=NULL) p2=p1,p1=p1->next; if(num==p1->num) {if (p1==head) head=p1->next; else p2->next=p1->next; printf("delete:%ld\n",num); n=n-1; } else printf("%ld not been found!\n",num); return(head); }
共同体(union) 共同体的含义与定义方法 共同体的存储特点 共同体的使用
共同体的含义与定义方法 一、含义: 几个不同变量共同占用同一块内存空间,只是一种覆盖技术,所谓的共同占用是指这几个变量共同拥有内存的同一个起始地址. 共用相同的存储单元。 二、定义形式: union 共用体名 例如: { 成员表列; union date }变量表列; { int i; char ch; float f;}a,b,c;
a a.i/a.ch/a.f b.i/b.ch/b.f c.i/c.ch/c.f b c 共同体的存储特点 例如: union date { int i; char ch; float f;}a,b,c; 1.同一块内存可以存放不同类型的数据,但在某一时刻只能存放其中的一种; 2.共用体变量中起作用的成员是最后一次存放的成员; 3.共用体变量的地址和它的成员的地址是同一个地址; 4.共用体变量不能整体被赋值,也不能给共用体变量赋初值; 5.不能把共用体变量作为函数的参数进行传递,但可以使用指向共用体变量的指针作为函数的参数; 6.结构体类型和共用体类型可以嵌套使用.
c的地址 ... ? 00000000 00110110 00000000 00111001 i[2] i[1] i[0] i[0]的低位地址 共同体的使用 例exp9_7:已知字符“0”的ASCII码为十六进制的30,下面程序的输出为: main() { union 运行结果:9 { unsigned char c; unsigned int i[4]; }z; z.i[0]=0x39; z.i[1]=0x36; printf(“%c\n”,z.c);}
例9_8:下列程序的运行结果是什么? main() { union zj {int a; char ch[2]; }au; au.a=298; printf("%d\n%d\n",au.ch[0],au.ch[1]); } 运行结果为:42,1
结构体与共用体小结 一、共同点: 都是不同类型数据的集合 二、不同点: 1、结构体中各个成员均在内存中存在, 而共同体中只有一个成员存在于内存中。 2、结构体占用的存储空间是所有成员所占空间的和; =sizeof(结构体名) 而共用体所占内存空间的大小是所有成员中占用存储空间最大的一个成员的占用空间的值。 3、结构体中各个成员相互独立、互不干扰, 共同体中改变一个成员的值,会影响到其它成员的值。 4、结构体与共同体可以互为成员。
枚举类型 1、含义: 如果一个变量只有几种可能的值,可以定义为枚举类型。 “枚举类型”是将变量的取值一一列举出来,变量的取值只限 在列出来的取值范围内. 2、定义方法: enum weekday {sun,mon,tue,wed,thu,fri,sat}; enum weekday workday; 枚举类型的变量 workday的取值只能在sun--sat之间. 如:workday=mon; 其中sun、mon、…、sat、等称为枚举元素或枚举常量。 它们是用户定义的标识符。这些标识符并不自动地代表什么含 义。 3、使用要点: 4、应用举例
要点: 1.枚举元素在c语言中按常量来处理,不是变量,不能被赋值; 2.作为常量的枚举元素,它们是有值的.在编译是按它们的定义顺序取值为0,1,2,3…… 也可以在定义类型时人为定义枚举元素的值,如 enum weekday {sun=7,mon=1,tue,wed,thu,fri,sat}; enum weekday workday; 3.枚举值可以用来做条件判断,如: if (workday==mon) … if (workday>tue) …枚举值的比较规则是:按其在定义时的顺序号比较。 4.一个整数不能直接赋值给一个枚举变量,如: workday=2;() workday=(enum weekday) 2; () 它相当干将顺序号为2的枚举元素赋给workday,相当于workday=tue; workday=tue; ()
例9_9:编写程序,功能是输入当天是星期几,就可以计算并输出n天后是星期几。例如,今天是星期六,若求3天后是星期几,则输入6,3,即输出“3天后是星期2”。例9_9:编写程序,功能是输入当天是星期几,就可以计算并输出n天后是星期几。例如,今天是星期六,若求3天后是星期几,则输入6,3,即输出“3天后是星期2”。 enum week{sun,mon,tue,wed,thu,fri,sat}; enum week nd(w,n); enum week w; int n; { return((enum week)(((int)w+n)%7));} main() { enum week w0,wn; / * w0表示当天的星期值,wn表示n天后的星期值 * / int n; scanf("%d,%d",&w0,&n); wn=nd(w0,n); if(wn==0) printf("%d天后是星期日\n",n); else printf("%d天后是星期%d\n",wn); }
案例:口袋中有红、黄、蓝、白、黑五种颜色的球若干个。每次从口袋中取出3个球,问得到三种不同色的球的可能取法,打印出每种组合的三种颜色。案例:口袋中有红、黄、蓝、白、黑五种颜色的球若干个。每次从口袋中取出3个球,问得到三种不同色的球的可能取法,打印出每种组合的三种颜色。 分析:球只能是5种色之一,而且要判断各球是否同色,应该用枚举类型变量处理。 设取出的球为i,j,k.根据题意,i,j,k分别是5种色球之一,并要求i!=j!=k.可以用穷举法,即一种可能一种可能地试,看哪一组符合条件。 用n累计得到三种不同色球的次数。外循环使第一个球i从red变到black。中循环使第二个球j也从red变到black。如果i和j同色则不可取;
只有i,j不同色(i!=j)时才需要继续找第三个球,此时第三个球k也有5种可能(red到black),但要求第三个球不能与第一个球或第二个球同色,即k!=i,k!=j.满足此条件就得到三种不同色的球。输出这种三色组合方案。然后使n加1。外循环全部执行完后,全部方案就已输只有i,j不同色(i!=j)时才需要继续找第三个球,此时第三个球k也有5种可能(red到black),但要求第三个球不能与第一个球或第二个球同色,即k!=i,k!=j.满足此条件就得到三种不同色的球。输出这种三色组合方案。然后使n加1。外循环全部执行完后,全部方案就已输 出完了。最后输出总数n. 为了输出3个球的颜色,显然应经过三次循环,第一次输出i的颜色,第二次输出j的颜色,第三次输出k的颜色。在三次循环中先后将I,j,k赋予pri。然后根据pri的值输出颜色信息。在第一次循环时,pri的值为1,如果i的值为red,则输出字符串“red”,其它的类推。 程序如下:
main() { enum color{red,yellow,blue,white,black}; enum color i,j,k,pri; int n=0,loop; for (i=red;i<=black;i++) for (j=red;j<=balack;j++) if (i!=j) {for (k=red;k<=black,k++) if ((k!=i)&&(k!=j)) {n=n+1; printf("%-4d",n); for (loop=1;loop<=3;loop++) {switch(loop) {case 1:pri=i;break; case 2:pri=j;break; case 3:pri=k;break; default:break;
} switch(pri) {casered:printf("%-10s,"red");break; case yellow:printf("%-10s","yellow");break; case blue:printf("%-10s","blue");break; case white:printf("%-10s","white");break; case black:printf("%-10s","black");break; default:break; } } printf("\n"); } } printf("\ntoal:%5d\n",n); }
运行结果如下: 1 red yellow blue 2 red yellow white 3 red yellow black 4 red blue yellow 5 red blue white 6 red blue black 7 red white yellow 8 red white blue 9 red white black 10 red black yellow 。。。。。。。。。。。。。。。。。 60black white blue tota1:60