72
第 8 第 第第 第第第第第第 第第 体、体 10.1 结结 结结 10.2结结 结结结 10.3 结结结结结结——结结 10.4结结结结 10.5结结 结结 10.6 结结结结结结结

第 8 章 结构体、枚举和共用体类型

  • Upload
    giza

  • View
    104

  • Download
    0

Embed Size (px)

DESCRIPTION

第 8 章 结构体、枚举和共用体类型. 10.1 结构体类型 10.2 结构体和函数 10.3 动态数据结构 —— 链表 10.4 枚举类型 10.5 共用体类型 10.6 用户自定义类型. 10.1 结构体类型. 结构体类型的定义 结构体类型中可以根据需要包含若干相同或不同类型的成员,这些成员所代表的信息必须是相关的信息。 结构体类型定义的格式为: struct 结构体名 { 类型名 成员名 1 ; 类型名 成员名 2 ; …… 类型名 成员名 n ; }; 例如, struct date - PowerPoint PPT Presentation

Citation preview

Page 1: 第 8 章 结构体、枚举和共用体类型

第 8 章 结构体、枚举和共用体类型

10.1 结构体类型10.2结构体和函数10.3 动态数据结构——链表10.4枚举类型10.5共用体类型10.6 用户自定义类型

Page 2: 第 8 章 结构体、枚举和共用体类型

10.1 结构体类型 结构体类型的定义 结构体类型中可以根据需要包含若干相同或不同类型的成员,

这些成员所代表的信息必须是相关的信息。 结构体类型定义的格式为:struct 结构体名{ 类型名 成员名 1 ; 类型名 成员名 2 ; …… 类型名 成员名 n ; };例如, struct date {int year; int month; int day; };

Page 3: 第 8 章 结构体、枚举和共用体类型

10.1 结构体类型struct student{int number; /* 学号 */ char name[8]; /* 姓名 */ char sex; /* 性别 */ float score[4]; /* 成绩 */}; struct 是结构体类型的标识,是关键字。 struct 和后面的结构体

名共同构成结构体类型名。结构体名应符合标识符的命名规则。 结构体所有成员的定义用花括弧括起来。结构体成员的定义方式

和变量的定义方式一样,成员类型可以是基本类型的,也可以是构造类型的。各成员之间用分号分隔。

结构体内的各个成员名不能重名,但成员名可以与结构体外其他标识符同名,并不产生冲突。

Page 4: 第 8 章 结构体、枚举和共用体类型

10.1 结构体类型 结构体变量的定义

用已经定义的结构体类型定义结构体变量。 struct student stu; 定义结构体类型的同时定义结构体变量。 stu1 , stu2 。 struct student

{int number; /* 学号 */

char name[8]; /* 姓名 */

char sex; /* 性别 */

float score[4]; /* 成绩 */

} stu1,stu2;

Page 5: 第 8 章 结构体、枚举和共用体类型

10.1 结构体类型 定义无名结构体类型的同时定义结构体变量。这种定义形式省

略了结构体名。不再需要定义此种类型变量的情况才可采用这种方式。

struct {int number; /* 学号 */ char name[8]; /* 姓名 */ char sex; /* 性别 */ float score[4]; /* 成绩 */}stu

结构体变量各个成员按照定义的顺序依次占用连续的空间。可以定义指针变量指向结构体类型变量。

结构体变量的地址虽与其第一个成员的地址的值是相同的,但它们的类型是不同的。它们之间不能直接相互赋值,但是可以先将其用强制类型转换,转换为同一类型指针后相互赋值。

Page 6: 第 8 章 结构体、枚举和共用体类型

10.1 结构体类型 结构体变量的初始化和引用 结构体变量的初始化

若在定义之后进行初始化操作,只能对每个成员单独进行赋值。 若在定义变量的同时进行初始化,则用一对花括弧括起各个成

员值的列表并用赋值号和变量连接,成员值之间逗号分隔,具体格式为:

结构体类型名 结构体变量 ={ 成员值列表 } ; 例如, struct student stu={1001, "wang", ‘f’, 60.5,80,75,

90}; 结构体变量的引用

只能引用结构体变量的成员,不能引用整个结构体变量。结构体变量的成员引用形式为:

结构体变量名 . 成员名 其中“ .” 称为成员运算符。如果是通过指向结构体变量的指

针引用结构体成员,形式为:( * 指针变量名) . 成员名 或 指针变量名 -> 成员名

Page 7: 第 8 章 结构体、枚举和共用体类型

10.1 结构体类型 如果结构体的成员仍然是构造类型,则需要逐级引用,直至最

低级的成员,即只能对结构体变量最低级的成员进行存取和运算。

【例 10.1 】输入学生的各项信息,计算总分和平均成绩后输出。#include "stdio.h"struct date{int year; int month; int day; }; /* 定义结构体类型 struct date*/struct student{int number; /* 学号 */ char name[10]; /* 姓名 */ struct date birthday; /* 生日, struct date 类型 */

Page 8: 第 8 章 结构体、枚举和共用体类型

10.1 结构体类型float score[4]; /* 四门课成绩 */float total; /* 总分 */float ave; /* 平均成绩 */ }; /* 定义结构体类型 struct student*/main( ){struct student stu,*p; int k; printf("please enter number& name: "); scanf("%d %s",&stu.number ,stu.name); printf("please enter birthday(year,month,day):"); scanf("%d%d%d",&stu.birthday.year, &stu.birthday.month, &stu.

birthday.day); printf("please enter the score(4):");

Page 9: 第 8 章 结构体、枚举和共用体类型

10.1 结构体类型 for(stu.total=0,k=0;k<4;k++) {scanf("%f",&stu.score[k]); stu.total+=stu.score[k]; /* 计算总成绩 */ } stu.ave= stu.total /4; /* 计算平均成绩 */ p=&stu; printf("number :%d\n", p->number);printf("name :%s\n", p->name);printf("birthday :%d,%d,%d\n", (*p).birthday.year, (*p).birthday.month, (*p).birthday.day);printf("score: "); for(k=0;k<4;k++)printf("%6.2f", p->score[k]);printf("\ntotal: %6.2f \n",stu.total);

Page 10: 第 8 章 结构体、枚举和共用体类型

10.1 结构体类型printf("average: %6.2f \n ",stu.ave);}某次程序运行结果为:please enter number& name: 1002 li↙please enter birthday(year,month,day):1975 6 8↙please enter the score(4):80 78.5 92 83.5↙number :1002name :libirthday :1975,6,8score: 80.00 78.50 92.00 83.50total:334.00average: 83.50

Page 11: 第 8 章 结构体、枚举和共用体类型

10.1 结构体类型 结构体数组 结构体数组的定义、初始化

若数组元素的类型为结构体类型,数组为结构体数组。 定义结构体数组的同时也可对数组进行初始化操作。例如,struct student{int number; /* 学号 */ char name[10]; /* 姓名 */ float score[4]; /* 四门课程成绩 */ float total; /* 总分 */ float ave; /* 平均成绩 */}stu[3]={ {461 , "liu",{80,78,67,80},0,0},{032,"geng",{98,78,86,9

0}, 0,0},{103, "qian",{79,89,68,80},0,0} }; 可以定义指向结构体数组元素的指针变量,然后通过指针对数

组元素操作。

Page 12: 第 8 章 结构体、枚举和共用体类型

10.1 结构体类型 结构体数组的引用

结构体数组元素也是通过数组名和下标来引用,但要注意只能对最低级的成员进行存取和运算。引用的一般形式为:

数组名 [ 下标 ]. 成员名例如, stu[1].number 、 stu[0].score[2] 、 stu[2].ave 通过指针引用结构体数组元素的形式和通过指针引用结构

变量形式一样,为:( * 指针变量名) . 成员名 或 指针变量名 -> 成员名 例如,语句“ p=&stu[1] ;”之后, (*p).number 、 (p-1)->

score[2] 、 p->ave;

Page 13: 第 8 章 结构体、枚举和共用体类型

10.1 结构体类型【例 10.2 】计算某班期末考试中所有学生的总分和平均成绩。#define N 50#include "stdio.h"struct student{int number; /* 学号 */ char name[10]; /* 姓名 */ float score[4]; /* 四门课程成绩 */ float total; /* 总分 */ float ave; /* 平均成绩 */};main( ){struct student stu[N],*p; int i,k; for(i=0, p=stu;p<stu +N;p++,i++) /* 输入学生的信息 */ { printf("the %d student\n ",i);

printf("number& name: ");

Page 14: 第 8 章 结构体、枚举和共用体类型

10.1 结构体类型scanf("%d %s",&p->number, p->name); printf("score(4): "); for(p->total=0,k=0;k<4;k++) { scanf("%f",&p->score[k]); p->total+=p->score[k]; /* 计算总成绩 */ } p-> ave = p->total /4; /* 计算平均成绩 */ } for (i=0;i<N;i++) /* 输出 */ {printf("number:%6d\n ", stu[i].number); printf("name:%s\n", stu[i].name); printf("score: "); for(k=0;k<4;k++) printf("%6.2f" , stu[i].score[k]); printf("\ntotal=%10.2f average=%10.2f \n", stu[i].total, stu[i].ave); } }

Page 15: 第 8 章 结构体、枚举和共用体类型

10.2 结构体和函数 结构体变量的成员作为函数参数 结构体变量的成员可作为函数的实际参数,但是不适合作函

数的形式参数。 结构体变量的成员作函数实参的用法与普通变量作函数实参

的用法相同,形参与实参之间仍然是“值传递”的方式。【例 10.3 】重新编写例 10.2 。#define N 50#include "stdio.h"struct student{int number; /* 学号 */ char name[10]; /* 姓名 */ float score[4]; /* 四门课程成绩 */ float total; /* 总分 */ float ave; /* 平均成绩 */ };

Page 16: 第 8 章 结构体、枚举和共用体类型

10.2 结构体和函数float total(float s[],int n) /* 计算总成绩 */{int k; float sum=0; for(k=0;k<n;k++) sum+=s[k]; return(sum); } float average( float x, int n ){return(x/n) ; /*返回平均值 */}main( ){struct student stu[N]; int i,k; for(i=0;i<N;i++) /* 输入学生的信息 */ { printf("the %d student\n ",i); printf("number& name: ");

Page 17: 第 8 章 结构体、枚举和共用体类型

10.2 结构体和函数 scanf("%d %s",&stu[i].number, stu[i].name); printf("score(4): "); for(k=0;k<4;k++) scanf("%f",&stu[i].score[k]); stu[i]. total = total(stu[i].score,4); /* 计算总成绩 */ stu[i].ave=average(stu[i].total,4); /* 计算平均成绩 */ } for (i=0;i<N;i++) /* 输出 */ {printf("number:%6d\n ", stu[i].number); printf("name: %s\n", stu[i].name); printf("score: ");

for(k=0;k<4;k++) printf("%6.2f" , stu[i].score[k]); printf("\ntotal=%10.2f average=%10.2f\n ", stu[i].total, stu[i].ave); } }

Page 18: 第 8 章 结构体、枚举和共用体类型

10.2 结构体和函数 结构体指针变量作为函数参数

允许函数之间传递结构体变量,若形参是结构体变量,那么实参也应是同类型的结构体。被调用函数对形参结构体变量的修改不能传递给实参。形参结构体变量占用内存空间、接收实参数据带来空间和时间的巨大开销。因此语法上虽然允许,但一般很少采用这种方式。

若向函数传递结构体变量的地址,则可以通过指针对实参结构体变量的空间操作,从而改变实参的值。参数传递时只需传递一个地址,空间和时间的代价都很小。

一般采用结构体指针作为函数参数,而不采用结构体变量作为函数参数。

Page 19: 第 8 章 结构体、枚举和共用体类型

10.2 结构体和函数【例 10.4 】重新编写例 10.3 。#define N 50#include "stdio.h"struct student{int number; /* 学号 */ char name[10]; /* 姓名 */ float score[4]; /* 四门课程成绩 */ float total; /* 总分 */ float ave; /* 平均成绩 */}; void input(struct student *stu) /* 输入学生信息 */{ int k; printf("number& name: "); scanf("%d %s",&stu->number,stu->name); printf("score(4): ");

Page 20: 第 8 章 结构体、枚举和共用体类型

10.2 结构体和函数for(k=0;k<4;k++) scanf("%f",&stu->score[k]); } void total_average(struct student *stu) /* 计算总成绩和平均值 */ { int k; stu->total=0; for(k=0;k<4;k++) stu->total+= stu->score[k]; stu->ave= stu->total/4; }void output(struct student *stu) /* 输出学生信息 */ { int k; printf("number:%6d\n ", stu->number); printf("name:%s\n", stu->name); printf("score: ");

Page 21: 第 8 章 结构体、枚举和共用体类型

10.2 结构体和函数for(k=0;k<4;k++) printf("%6.2f" , stu->score[k]);printf("\ntotal=%10.2f average=%10.2f \n", stu->total, stu->ave);}main( ){struct student stu[N],*p; int i; for(i=0;i<N;i++) /* 输入学生的信息 */ {printf("the %d student\n",i); input(&stu[i]); total_average(&stu[i]) ; /* 计算平均成绩 */ } printf("\n"); for (i=0;i<N;i++) /* 输出 */ output(&stu[i]);}

Page 22: 第 8 章 结构体、枚举和共用体类型

10.2 结构体和函数 结构体数组作函数参数

向函数传递结构体数组实际也是传递数组的首地址。形参数组与实参数组使用共同的内存单元。

函数形参、实数是同类型的结构体数组名或结构体指针。【例 10.5 】重新编写例 10.4 。#define N 50#include "stdio.h"struct student{int number; /* 学号 */ char name[10]; /* 姓名 */ float score[4]; /* 四门课程成绩 */ float total; /* 总分 */ float ave; /* 平均成绩 */ };

Page 23: 第 8 章 结构体、枚举和共用体类型

10.2 结构体和函数void input(struct student stu[ ],int n)/* 输入学生的信息 */{ int i,k; for(i=0;i<n;i++) { printf("the %d student\n ",i); printf("number& name: "); scanf("%d %s",&stu[i].number ,stu[i].name); printf("score(4): "); for(k=0;k<4;k++) scanf("%f",&stu[i].score[k]); } }

void total_average(struct student *stu,int n) /* 计算总成绩和平均值 */{int k; struct student *p; p=stu+n;

Page 24: 第 8 章 结构体、枚举和共用体类型

10.2 结构体和函数for(;stu<p;stu++) {stu->total=0; for(k=0;k<4;k++) stu->total+= stu->score[k]; stu->ave= stu->total/4; } }void output(struct student *stu, int n) /* 输出处理后的学生信息 */{ int k,i; for (i=0;i<n;i++) { printf("number:%6d\n ", (stu+i)->number); printf("name:%s\n", (stu+i)->name); printf("score: ");

Page 25: 第 8 章 结构体、枚举和共用体类型

10.2 结构体和函数 for(k=0;k<4;k++) printf("%6.2f" , (stu+i)->score[k]); printf("\ntotal=%10.2f average=%10.2f \n", (stu+i)->total, (stu+i) -

>ave); }}main( ){struct student stu[N]; struct student *q=stu; input(q,N); total_average(stu,N); output(stu,N); }

Page 26: 第 8 章 结构体、枚举和共用体类型

10.3 动态数据结构——链表 内存空间的动态分配和释放 free 函数

free 函数的的格式:void free(void *p); free 函数的功能:释放指针变量 p所指向的内存区,该函数没

有返回值。一般 p的值是调用 malloc 函数或 calloc 函数的返回值。

malloc 函数 malloc 函数的格式:void *malloc(unsigned int size); malloc 函数的功能:在内存的动态存储区中分配 size 个字节

的连续空间,并返回分配空间的起始地址。若分配失败,则返回空指针。

注意: malloc 函数的返回值一般是空类型的指针(有的编译系统将其处理成 char 类型的指针)。使用该函数时通常需要用强制类型转换,将其转换为所需要的类型。

Page 27: 第 8 章 结构体、枚举和共用体类型

10.3 动态数据结构——链表【例 10.6 】注意内存空间的分配和释放。main( ){double *pointer; pointer=(double *)malloc(sizeof(double)); /*申请一个 double 类型变

量所需内存空间 */*pointer=8.234;printf(“%6.4f\n”,*pointer);*pointer=*pointer+10;printf("%7.4f\n",*pointer);*pointer=*pointer/2;printf("%6.4f",*pointer);free(pointer); /*释放空间 */}

Page 28: 第 8 章 结构体、枚举和共用体类型

10.3 动态数据结构——链表程序运行结果为:8.234018.23409.1170 calloc 函数

calloc 函数的的格式: void *calloc(unsigned int n,unsigned int size); calloc 函数的功能:分配内存的动态存储区中 n 个长度为 size 个

字节的连续空间,并返回分配空间的起始地址。若分配失败,则返回一个空指针。对 calloc 的返回值同样也可进行强制类型转换后使用。

注意: calloc 函数常用来为一维数组分配空间,其中 n 表示数组的长度, size 表示数组元素所占字节数。 这种方式获得的数组一般称为动态数组。

Page 29: 第 8 章 结构体、枚举和共用体类型

10.3 动态数据结构——链表【例 10.7 】注意内存空间的分配和释放。main( ){int *a,i;a=(int *)calloc(5,sizeof(int)); /*申请 5 个整型变量所需内存空间 */for(i=0;i<5;i++) *(a+i)=i+1;for(i=0;i<5;i++) printf("%4d",*(a+i));free(a); /*释放空间 */}程序运行结果为: 1 2 3 4 5

Page 30: 第 8 章 结构体、枚举和共用体类型

…… NULL

head

10.3 动态数据结构——链表 链表 链表由若干个结点构成。一个结点就是一个结构类型的变量。

链表的每个结点需要占用连续内存空间,但是整个链表无须占用连续空间。因此,结点成员应该包括两部分:一部分存放实际需要的数据信息,称为数据域;一部分存放指针,用来链接各个结点,称为链接域或指针域。

在链表中,一个结点后面相邻的结点称为该结点的后继结点,该结点则称为其后继结点的前驱结点。

单链表:结点指针域只包含一个后继结点地址的链表。用一个指针变量保存单链表的首地址,即第一个结点的地址,这个指针变量被称为头指针。头指针是一个链表的标志。

注意:在链表的定义中,指向前驱结点或后继结点的指针的基类型是结点自身类型。

Page 31: 第 8 章 结构体、枚举和共用体类型

【例 10.8 】通过给结点的指针域赋值建立一个单链表。#include "stdio.h"struct node{ char c ; struct node *next; }main( ){ struct node *n1,*n2,*n3,*head,*p; /*申请三个结点空间 */ n1=(struct node* )malloc(sizeof(struct node)); n2=(struct node* )malloc(sizeof(struct node)); n3=(struct node* )malloc(sizeof(struct node)); /*建立链表 */ head=n1;

10.3 动态数据结构——链表

Page 32: 第 8 章 结构体、枚举和共用体类型

10.3 动态数据结构——链表 n1->c= 'A '; n1->next=n2;

n2->c= 'B '; n2->next=n3; n3->c= 'C '; n3->next=NULL; for(p=head;p!= NULL;p=p->next) printf("%3c",p->c); }

Page 33: 第 8 章 结构体、枚举和共用体类型

10.3 动态数据结构——链表 链表的基本操作 为了简化链表的各种操作,一般在含有有效信息的所有结

点之前再附加一个数据域不包含任何信息的结点。这个结点被称为“头结点”。头结点的存在是以一个结点空间为代价换取程序编写的简单明了。

下面的各项链表操作都是针对有头结点的链表编写的,结点的类型均为:

struct node{int data; struct node *next;};并假定成员 data 的值不可能为负数。

Page 34: 第 8 章 结构体、枚举和共用体类型

10.3 动态数据结构——链表 创建链表采用从表尾插入新结点的方式,则创建链表的主要步骤为:① 生成只含有头结点的空链表,头指针 head 和尾指针 tail (指向

链表的最后一个结点)均指向头结点。② 读取数据信息,申请新的结点空间,将数据存于新结点。将新结点插入到链表最后一个结点的后面, tail指针指向新插入的结点,重复②的操作直到输入结束。 ③尾结点指针域赋 NULL。【例 10.9 】编写函数,在表尾插入结点生成单链表。struct node *createtail( ){struct node *head,*p,*tail; int data; head=(struct node* )malloc(sizeof(struct node)); /* 生成头结点 */ tail=head; /*尾指针指向最后一个结点 */ scanf("%d",&data); /*读取数据 */

Page 35: 第 8 章 结构体、枚举和共用体类型

10.3 动态数据结构——链表while(data>=0) { p=(struct node* )malloc(sizeof(struct node)); /* 生成新

结点 p*/ p->data= data; /*读入的数据存放到结点 p 的数据域 */

tail->next=p; /* 将新结点 p插入到表尾 */ tail=p; /*修改尾指针 tail 指向当前的尾结点 */

scanf("%d",&data); /*读入下一个数据 */ } tail->next=NULL; /* 将最后一个结点的指针域置为

空 */ return(head); /*返回头指针 */}

Page 36: 第 8 章 结构体、枚举和共用体类型

10.3 动态数据结构——链表

图 (a) 图 (b)

图 (c)

图 (d)

head

tail

head

tail

56

p

head 56

tail

p

head 56 4

tail

p

Page 37: 第 8 章 结构体、枚举和共用体类型

10.3 动态数据结构——链表

图 (e)

head 56 4 8 NULL

tail

p

Page 38: 第 8 章 结构体、枚举和共用体类型

10.3 动态数据结构——链表若采用从表头插入新结点的方式,则创建链表的主要步骤为:① 生成只含有头结点的空链表,定义头指针 head 指向头结点。② 读取数据信息,申请新的结点空间,将数据存于新结点。将新

结点插入到头结点后面。重复②的操作直到输入结束。 【例 10.10 】编写函数,在表头插入结点生成单链表。struct node * createhead( ){struct node *head,*p; int data; head=(struct node *)malloc(sizeof(struct node)); /* 生成头结点 */ head->next=NULL; /*置为空表 */ scanf(“%d”,&data); /*读取数据 */ while(data>=0) {p=(struct node* )malloc(sizeof(struct node)); /* 生成新结点p

*/ p->data=data; /*读入的数据存放到结点p的数据域 */

Page 39: 第 8 章 结构体、枚举和共用体类型

10.3 动态数据结构——链表 p->next=head->next; /* 将新结点插入到头结点之后 */

head->next=p; /*修改头结点的指针域指向新插入结点 */

scanf("%d",&data); /*读入下一个数据 */

}

return(head); /*返回头指针 */

}

图 (a) 图 (b)

head NULL

head

s 56 NULL

Page 40: 第 8 章 结构体、枚举和共用体类型

10.3 动态数据结构——链表

图(c)

图 (d)

head 56 NULL

s 4

① ②

head 8 4 56 NULL

Page 41: 第 8 章 结构体、枚举和共用体类型

10.3 动态数据结构——链表【例 10.11 】编写函数,输出单链表内容。void output(struct node *head) /* 输出链表 */

{struct node *p;

printf("the linklist is:\n");

p=head->next; /* 指针变量 p获得头结点后继结点的地址 */

while (p!=NULL) /*判断指针变量 p 是否为 NULL ,为NULL 表示链表结束 */

{printf("%6d",p->data); /* 输出指针变量 p 结点的内容 */

p=p->next; /* 指针变量 p 指向链表下一个结点 */

}

return;

}

Page 42: 第 8 章 结构体、枚举和共用体类型

10.3 动态数据结构——链表删除结点在单链表中删除指定结点的主要步骤为: ①找到要删除的 p结点和 p结点的前驱结点 q(由于单向

链表有头结点,所以前驱结点一定存在)。 ②删除该结点:修改前驱结点的指针域指向待删除结点的后继结点,并释放被删除结点所占用的空间。

注意:删除结点后,一定要释放被删除结点所占空间。否则,该结点会成为游离的、不可再用的空间。

…… ……

q p

Page 43: 第 8 章 结构体、枚举和共用体类型

【例 10.13 】编写函数,在单链表中删除值为 x 的结点。struct node * delete(struct node *head, int x )/*从链表中删除值为 x 的结点 */

{struct node *p,*q; q=head; /*q 指向头结点 */ p=head->next; /* p 指向第一个结点 */ while(p&&(p->data!=x)) /*查找值为的结点 p ,查找过程中, q 指向 p

结点的前驱 */{q=p;p=p->next ;} if(p) {q->next =p->next ;free(p);} /* 若找到, q 结点的指针域指向 p 结点的后继,释放 p*/

else {printf("not find! \n ");} /* 若未找到,输出相应的信息 */ return(head); /*返回头指针 */}

10.3 动态数据结构——链表

Page 44: 第 8 章 结构体、枚举和共用体类型

10.3 动态数据结构——链表插入结点插入到p结点之后,相应 C语句为:

① s->next=p->next; s 的指针域指向 p 的后继结点。 ② p->next=s; p 的指针域指向新插入结点 s 。

……

p

s

① ②

Page 45: 第 8 章 结构体、枚举和共用体类型

10.3 动态数据结构——链表 在结点p之前插入新结点,首先要找到p的前驱结点q。

相应 C语句为:

① s->next=p; s 的指针域指向 p 。 ② q->next=s; q 的指针域指向新插入结点 s 。

q p

…… ……

s

① ②

Page 46: 第 8 章 结构体、枚举和共用体类型

10.3 动态数据结构——链表【例 10.14 】编写函数,在单链表中值为 x 的结点之前插入一个

值为 y 的结点。struct node * insert(struct node *head,int x,int y)/* 在链表值为 x 的

结点前插入值为 y 的结点 */{struct node *p,*q,*s; s=(struct node *)malloc(sizeof(struct node)); /* 生成新结点 s*/ s->data=y; /* 将 y 存放在新结点中 */ q=head; /*q 指向头结点 */ p=head->next; /* p 指向第一个结点 */ while((p->data!=x)&&p) /*寻找值为 x 的结点 , 若表非空且未找到时循环 */

{q=p; p=p->next;} /*保持 p 指向当前结点, q 指向 p 结点的前驱 */

Page 47: 第 8 章 结构体、枚举和共用体类型

10.3 动态数据结构——链表 if(p==NULL) /*未找值为 x 的结点,返回空 */

{printf("not find x\n");

free(s);

return (head);

}

s->next=p; /*插入 s 结点到 p 之前 */

q->next=s;

return(head); /*返回头指针 */

}

Page 48: 第 8 章 结构体、枚举和共用体类型

10.3 动态数据结构——链表【例 10.15 】链表的综合操作。#inlude "data.h"#inlude "func.c"#inlude "stdio.h"main( ){struct node *head,*p; int x,a,b; char ch; do{ printf("\n1:create\n2:search\n3:insert\n4:delete\n5:output\n0:exit\n"); printf("please select: "); ch=getch( ); while(!(ch<='5'&&ch>='0')) ch=getch(); /* 输入不在 0到 5 之间无效

*/ putchar(ch); printf("\n ");

Page 49: 第 8 章 结构体、枚举和共用体类型

10.3 动态数据结构——链表 switch(ch) { case '1': /* 若选择 1 ,创建链表 */ printf("please enter data: "); head=createhead( ); /*创建链表 */ output(head); /* 输出创建后的链表 */ break; case '2': /* 若选择 2 ,在链表中查找数据 */ printf("please enter data: "); scanf("%d",&x); /* 输入待查找的数据 */ p=search(head,x); /* 进行查找 */ if(p!=NULL)printf("\nthe data is:%d ",p->data); else printf("\n not found"); break;

Page 50: 第 8 章 结构体、枚举和共用体类型

10.3 动态数据结构——链表 case '3': /* 若选择3,在链表中插入数据 */ printf("please enter data(insert b before a)a,b: "); scanf("%d %d",&a,&b); /* 输入插入位置及数据 */ head=insert(head,a,b); /* 进行插入 */ output(head); /* 输出插入后的链表 */ break; case '4': /* 若选择 4 ,在链表中删除数据 */ printf("please enter the data to delete: "); scanf("%d",&a); /* 输入要删除的数据 */ head=delete(head,a); /* 进行删除 */ output(head); /* 输出删除后的链表 */ break; case '5': /* 若选择 5 ,输出链表数据 */ output(head); }

Page 51: 第 8 章 结构体、枚举和共用体类型

10.3 动态数据结构——链表 }while(ch!= '0');

}

Page 52: 第 8 章 结构体、枚举和共用体类型

10.4 枚举类型 枚举类型的定义 枚举类型的定义格式如下:enum 枚举类型名 { 枚举常量 1 ,枚举常量 2 ,……,枚举常量 n} ;

enum week{Sunday , Monday ,Tuesday ,Wednesday ,Thursday ,Friday , Saturday };

enum 是用来定义枚举类型的关键字,枚举类型名应符合标识符定义的规则, enum 和枚举类型名共同构成枚举类型的类型名。

花括弧内是该枚举类型所有可能的取值列表,也就是枚举常量列表。每个枚举常量都是有意义的标识符;

枚举常量表面上是标识符,实际存储的是整型值。在系统默认情况下,第一个枚举常量的值是 0 ,以后每个枚举常量的值是前一个常量的值加 1 。枚举常量的值在定义后不能被修改。

枚举常量也可显示赋值,未被显式赋值的枚举常量的值是前一个常量值加 1 。

enum week{Sunday=7 , Monday =1,Tuesday ,Wednesday ,Thursday ,Friday , Saturday };

Page 53: 第 8 章 结构体、枚举和共用体类型

10.4 枚举类型 枚举类型变量的定义和引用

枚举类型变量的定义 定义枚举类型的同时定义变量。enum colors{red,blue,green,yellow} c1,c2[20]; 先定义枚举类型,然后定义变量。enum colors{red,blue,green,yellow} ;enum colors c1,c2[20]; 如果之后不再使用此枚举类型,可定义类型时省略枚举类型名并同时定

义变量。enum {red,blue,green,yellow} c1,c2[20];

枚举类型变量的引用 枚举类型变量的取值范围只能是其枚举类型定义时所枚举的枚举常量。

枚举常量可赋值给枚举变量。整型和枚举类型之间不能相互赋值,若要赋值,需进行强制类型转换。

Page 54: 第 8 章 结构体、枚举和共用体类型

10.4 枚举类型【例 10.16 】考虑程序执行结果,体会枚举类型的用法。main( )

{enum colors{red,blue,green,yellow} c1,c2;

c1=blue;

c2=( enum colors)1;

if(c1==c2)printf("yes");

else printf("no");

}

程序运行结果为:yes

Page 55: 第 8 章 结构体、枚举和共用体类型

10.4 枚举类型 可以比较枚举类型变量的大小,枚举变量的比较相当于它们所隐含整数值的比较。枚举变量可以作为循环变量来控制循环。

【例 10.17 】考虑程序执行结果,体会枚举类型的用法。enum week{Sunday , Monday ,Tuesday ,Wednesday ,Thursday ,Frida

y , Saturday };main( ){enum week w; int k=0; for(w= Sunday;w<= Saturday;w++,k++) printf("%2d",k); }程序运行结果为: 0 1 2 3 4 5 6

Page 56: 第 8 章 结构体、枚举和共用体类型

10.4 枚举类型 枚举类型变量无法直接读入。枚举类型直接输出也只是显示整数,而不能显示标识符形式的枚举常量。

【例 10.18 】仔细阅读下面例题,考虑程序执行结果,体会枚举类型的用法。

enum week{sun,mon,tue,wed,thu,fri,sat};main( ){ enum week w,m; int day; char *name[]={"Sunday", "Monday", "Tuesday", "Wednesday", "Thursd

ay", "Friday", "Saturday" }; printf("please enter what day today(0-sun,1-mon,2-tue,3-wed,4-thu,5-fri,

6-sat): "); scanf("%d",&day); w=(enum week)day;

Page 57: 第 8 章 结构体、枚举和共用体类型

10.4 枚举类型for(m=w;m!=(w+6)%7;m=(m+1)%7)

printf(" %s",name[m]);

}运行时输入 4 ,程序运行结果为:please enter what day today(0-sun,1-mon,2-tue,3-wed,4-thu,5-f

ri,6-sat):4↙

Thursday Friday Saturday Sunday Monday Tuesday

Page 58: 第 8 章 结构体、枚举和共用体类型

10.5 共用体类型 共用体类型的定义 共用体类型定义的格式为:union 共用体名{ 类型名 成员名 1 ; 类型名 成员名 2 ; …… 类型名 成员名 n ;} ; union 是关键字,标识共用体类型。共用体名应符合标识符

的命名规则, union 和共用体名共同构成共用体类型名。 共用体的所有成员共占一段内存。某个时刻只有一个成员起

作用,其它成员无效。 整个共用体所占的内存大小等于占用内存单元最多的那个成

员所占用的空间。 花括弧中共用体成员定义和结构体成员定义方式相同。共用

体类型的成员可以是构造类型,但通常情况下是基本类型的。

Page 59: 第 8 章 结构体、枚举和共用体类型

10.5 共用体类型 共用体变量的定义和引用

变量的定义 定义共用体类型的同时定义变量。 先定义共用体类型,然后定义共用体变量。 如果之后不再使用此共用体类型,可定义类型时省略共用

体名并同时定义变量。union example{char c1; int c2; float c3;}un1,un2;

Page 60: 第 8 章 结构体、枚举和共用体类型

10.5 共用体类型union example{char c1; int c2; float c3;};uinon example un1,un2;

union { char c1; int c2; float c3;}un1,un2;

Page 61: 第 8 章 结构体、枚举和共用体类型

10.5 共用体类型 变量的引用 引用共用体变量时,一定要记住某一时刻只能有某一个成员起

作用。只能引用共用体变量的成员,而不能引用整个共用体变量。引用形式为:

共用体变量名 . 成员名un1.c1 , un2.c2 , un1.c3 是合法的引用。 可通过指向共用体的指针变量引用共用体变量,引用形式为:共用体指针变量 - 〉成员名 或 (* 共用体指针变量) . 成员名语句“ union un *pu;pu=&un1;” 之后, (*pu).c , pu ->k是合法的引用。

说明: 给共用体变量赋值时,只能对共用体的一个成员赋值,而

不能对整个共用体变量赋值。对共用体变量而言,只有最近一次被赋值的成员是有效的,其他成员无效。

Page 62: 第 8 章 结构体、枚举和共用体类型

10.5 共用体类型 共用体变量不能在定义时初始化。 若共用体的成员是构造类型的,则需要逐级引用至最

低级的成员。 共用体变量的地址和共用体各个成员的地址是相同的。

但是它们的类型是不同的。【例 10.19】学校某个协会既有学生成员又有教师成员,学

生信息包括姓名、年龄、职业(学生‘ S’)和年级,教师信息包括姓名、年龄、职业(教师‘ T’)和职称。输入协会成员列表,并输出年龄小于 25岁的成员信息。

Page 63: 第 8 章 结构体、枚举和共用体类型

10.5 共用体类型#define N 5#include "stdio.h"struct associator{char name[10]; /* 姓名 */ int age; /*年龄 */ char job; /*职业 */ union /*年级、职称的公用体 */ {int grade; char position[10]; }category; };

Page 64: 第 8 章 结构体、枚举和共用体类型

10.5 共用体类型main( ){ struct associator member[N]; int i; printf("please enter associators:name age job grade/position:\n"); for(i=0;i<N;i++) /* 输入数据 */ {scanf("%s %d %c",member[i].name, &member[i].age,&member[i].job); if(member[i].job=='S')scanf("%d",&member[i]. category.grade); else scanf("%s",member[i]. category.position); }

printf("associators(age<25):\n"); for(i=0;i<N;i++) /* 输入数据 */ if(member[i].age<25) { printf("%s %d %c",member[i].name, member[i].age,member[i].

job);

Page 65: 第 8 章 结构体、枚举和共用体类型

10.5 共用体类型if(member[i].job=='S') printf (" %d\n",member[i]. category.grade); else printf(" %s\n",member[i]. category.position); }}输入 5条信息,运行结果为:please enter associators:name age job grade/position:li 50 T professor↙wang 33 T professor↙zhang 24 T lector↙zhao 21 S 3↙liu 26 S 1↙associators(age<25):zhang 24 T lectorzhao 21 S 3

Page 66: 第 8 章 结构体、枚举和共用体类型

10.6 用户自定义类型 用 typedef 声明基本类型定义基本类型的新类型名的形式为:typedef 基本类型名 新类型名;例如, typedef char CHARACTER;

CHARACTER ch[80],c;

用 typedef 声明构造类型 数组类型的声明

定义数组类型的新类型名的形式为;typedef 新类型名 [ 数组长度 ] ;例如, typedef char STRING[80]; STRING str1 ,str2;

Page 67: 第 8 章 结构体、枚举和共用体类型

10.6 用户自定义类型 结构体、共用体类型的声明类型关键字( struct 或 union )类型名 { 类型名 成员名 1 ; 类型名 成员名 2 ; …… 类型名 成员名 n ; } ; typedef 类型关键字( struct 或 union )类型名 新类

型名 ;

Page 68: 第 8 章 结构体、枚举和共用体类型

10.6 用户自定义类型【例 10.20 】注意类型 DATA使用。

struct data{int year; int month; int day;};typedef struct data DATA;main( ){DATA d; d.year=1990; d.month=7; d.day=9; printf("year=%dmonth=%dday=%d",d.year,d.month,d.day);}程序运行结果为:year=1990month=7day=9

Page 69: 第 8 章 结构体、枚举和共用体类型

10.6 用户自定义类型 typedef 类型关键字( struct 或 union )类型名 { 类型名 成员名 1 ; 类型名 成员名 2 ; ……

类型名 成员名 n ; } 新类型名;typedef struct data

{int year;

int month;

int day;

}DATA;

Page 70: 第 8 章 结构体、枚举和共用体类型

10.6 用户自定义类型 typedef 类型关键字( struct 或 union ) { 类型名 成员名 1 ; 类型名 成员名 2 ; ……

类型名 成员名 n ; } 新类型名;

Page 71: 第 8 章 结构体、枚举和共用体类型

10.6 用户自定义类型【例 10.21 】注意类型 TEST使用。typedef union{int x; float y;}TEST; main( ){TEST d; d.x=99; printf("d=%d\n",d.x); d.x=1.99; printf("d=%f\n",d.y);}程序运行结果为:d=99d=1.990000

Page 72: 第 8 章 结构体、枚举和共用体类型

10.6 用户自定义类型 枚举类型的声明

enum 枚举类型名 { 枚举常量列表 } ; typedef enum 枚举类型名 新类型名;例如, enum colors{red,blue,green,yellow} ; typedef enum colors COLOR; typedef enum 枚举类型名 { 枚举常量列表 } 新类型名;例如, typedef enum colors{red,blue,green,yellow} COLOR; typedef enum { 枚举常量列表 } 新类型名;例如, typedef enum {red,blue,green,yellow} COLOR;

用户自定义类型的用途 减少书写工作量,增加程序可读性。 提高程序的可移植性。