390 likes | 550 Views
第九章 结构体 概述 在实际应用中,有不少应用问题如果只采用已学的变量和数组作为数据结构显得很不方便。 例:输入 100 个学生的学号、姓名和考试成绩,编写程序找出高分者和低分者。 用变量和数组作数据结构 可编写程序如下:. main() { int i, int num, maxnum, minnum; char name[20], maxname[20], minname[20]; int score, maxscore, minscore; maxscore=0; minscore=100;
E N D
第九章 结构体 概述 在实际应用中,有不少应用问题如果只采用已学的变量和数组作为数据结构显得很不方便。 例:输入100个学生的学号、姓名和考试成绩,编写程序找出高分者和低分者。 用变量和数组作数据结构可编写程序如下:
main() { int i, int num, maxnum, minnum; char name[20], maxname[20], minname[20]; int score, maxscore, minscore; maxscore=0; minscore=100; for(i=1; i<=100; i++) { scanf(%d%s%d”,&num,name,&score); if(score>maxscore) { maxscore=score; maxnum=num; strcpy(maxname,name); } if(score<minscore) {minscore=score; minnum=num; strcpy(minname,name);} } 输出 }
明显缺点: ①变量过多,同一学生的各个数据无联系,没有整体概念,不便管理。 ②操作不便(如更新过程)。 显然,选用一种能把一个学生的数据构造成一个整体的构造型数据结构更合适,但不能是数组。 对于这种情况,可以将一个学生的数据定义为一个结构体类型:
struct student 类型名 { int num; 成员表 char name[20]; int score; }; 定义了一个结构体类型,它包含三个成员。
9.2 定义结构体类型变量的方法 前面定义的结构体类型只是一种“模型”,还必须定义结构体变量后才能存放数据。 定义结构体变量有三种方法:
1、先定义结构体类型再定义结构体变量 struct student { int num; char name[20]; int score; }; 定义了结构体类型后: struct student st, stmax, stmin; 类型符 变量名 定义了三个结构体变量,每个变量包含三个成员,每个变量可存放一个学生的数据。
2、在定义结构体类型的同时定义结构体变量 struct student { int num; char name[20]; int score; }st, stmax, stmin;
3、直接定义结构体类型变量 struct 不出现类型名 { int num; char name[20]; int score; }st, stmax, stmin; 常用第一种方法
说明: ①类型与变量不同,只对变量分配空间与操作。 ②对成员可以单独使用,相当于普通变量。 ③成员也可以是一个结构体变量。 struct date struct student { int month; { int num; int day; char name[20]; int year; struct date birthday; };}st1, st2; ④成员名可以与程序中的变量名相同,两者代表不同的对象。
9.3 结构体变量的引用■成员引用 可以对成员单独引用,形式为: 结构体变量名. 成员名 成员运算符 st.num=1001;st.score=90;strcpy(st.name,”Li”); printf(“%d%s%d”,st.num,st.name,st.score); scanf(“%d%s%d”,&st.num,st.name,&st.score) ; 可以引用成员的地址
如果成员本身又属一个结构体类型,则要用若干个成员运算符,一级一级地找到最低一级的成员,只能对最低级的成员进行存取与运算。如果成员本身又属一个结构体类型,则要用若干个成员运算符,一级一级地找到最低一级的成员,只能对最低级的成员进行存取与运算。 st1.birthday.year=1960; st1.birthday.month=5; st1.birthday.day=15;
■整体引用 可以对结构体变量进行整体赋值: stmax=st;将st中的所有内容赋值给stmax。 对结构体变量的整体操作只限于赋值操作和参数传递,而且要求类型一致。不能对结构体变量进行整体输入输出。
结构体应用举例: 编写程序输入100个学生的学号、姓名和考试成绩,找出高分者和低分者。 struct student { int num; char name[20]; int score; };
main() { int i; struct student st,stmax,stmin; stmax.score=0; stmin.score=100; for(i=1;i<=100;i++) { scanf(“%d%s%d”,&st.num,st.name,&st.score); if(st.score>stmax.score) stmax=st; if(st.score<stmin.score) stmin=st; } printf(“\n%5d%15s%5d”,stmax.num,stmax.name, stmax.score); printf(“\n%5d%15s%5d”,stmin.num,stmin.name, stmin.score); }
9.4 结构体变量的初始化 对结构体变量可以在定义时指定初始值struct student { int num; char name[20]; int score; }st={1001,”wang”,95};
9.5 结构体数组可以定义结构体数组来存放批量数据。 ■结构体数组的定义 struct student { int num; char name[20]; int score; }; struct student a[100]; 定义a数组,可以存放100个学生的数据。 a数组的每个元素又是一个结构体变量。
■结构体数组的初始化 在定义结构体数组的同时指定初值。 struct student { int num; char name[20]; int score; }; struct student a[2]= {{1001,”LiLi”,85},{1002,”wang”,90}};
或: struct student { int num; char name[20]; int score; } a[2]= {{1001,”LiLi”,85},{1002,”wang”,90}};
■结构体数组元素的引用 成员引用: a[0].num=1001; strcpy(a[0].name,”wang”); a[0].score=85; 整体引用: a[1]=a[0]; 与普通数组元素的引用相同
■结构体数组的应用 输入100个学生的学号、姓名和考试成绩,然后按从高分到低分的顺序排列后输出。
struct student { int num; char name[20]; int score; }; main() { int i, j; struct student a[100], t; for(i=0;i<100;i++) scanf(“%d%s%d”,&a[i].num,a[i].name, &a[i].score);
for(i=0;i<99;i++){ for(j=i+1; j<100; j++){ if(a[i].score<a[j].score) { t=a[i]; a[i]=a[j]; a[j]=t;} 整体引用 }} for(i=0;i<100;i++) printf((“\n%5d%15s%5d”,a[i].num, a[i].name,a[i].score); }
定义指向结构体类型数据的指针变量p 9.6 指向结构体类型数据的指针 ■指向结构体类型变量的指针 struct student st, st1; struct student *p; p=&st;
通过指针变量引用结构体变量: ①成员引用 (*p).num=1001;或p->num=1001; (*p).score=85;或p->score=85; strcpy((*p).name,”wang”); 或strcpy(p->name,”wang”); ②整体引用 st1=*p;等效于st1=st;
■指向结构体数组的指针 struct student a[100]; struct student *p; p=a; 通过指针变量引用结构体数组元素: ①成员引用 (*p).num=1001;或p->num=1001; (*p).score=85;或p->score=85; strcpy((*p).name,”wang”); 或strcpy(p->name,”wang”);
一般地: (*(p+i)).num=1001;或(p+i)->num=1001; (*(p+i)).score=85;或(p+i)->score=85; strcpy((*(p+i)).name,”wang”); 或 strcpy((p+i)->name,”wang”); 也可以用下标法:p[i].num=1001; ②整体引用 *(p+1)=*(p+0);或 p[1]=p[0];
■用结构体变量和指向结构体的指针作函数参数 ■用结构体变量和指向结构体的指针作函数参数 用结构体变量作函数参数时,对应的实参应该是同类型的结构体变量(或数组元素),参数传递是“值传递”。 用指向结构体的指针作函数参数时,对应的实参应该是同类型的结构体变量的地址(或数组的地址),参数传递是“地址传递”。
st 1001 LiLi 70 a 1001 LiLi 70 main() { struct student st={1001,”LiLi”,70}; f(st); printf(“\n %5d%10s%5d”, st.num,st.name,st.score); } f(struct student a) { a.score=90; printf(“\n %5d%10s%5d”, a.num,a.name,a.score); } 90
st 2000 1001 LiLi 70 90 a 2000 main() { struct student st={1001,”LiLi”,70}; f(&st); printf(“\n %5d%10s%5d”,st.num,st.name,st.score); } f(struct student *a) { a->score=90; printf(“\n%5d%10s%5d”,a->num,a->name,a>score); } 通过指针变量a可以访问它所指向的结构体。
9.7 用指针处理链表 ■链表概述 链表是一种重要的数据结构─动态数据结构。 以具体例子来说明链表的概念及其应用: 例:选择合适的数据结构来存放一批学生的学号及考试成绩,以便进一步处理。 由于学生人数未知,用静态数据结构不合适。 用链表处理较恰当。
学号 成绩 指针 学号 成绩 指针 学号 成绩 指针 学号 成绩 指针 学号 成绩 NULL head 用链表处理该问题的基本思路: 将各学生的数据进行离散存放,来一个学生就分配一小块内存(结点)。并将各结点用指针依次连接起来─链表。 每结点应包含下一结点的开始地址。 最后一个结点中的指针为空。 链头指针指向第一个结点,是访问链表的重要依据。 这样的链表称单向链表。
一个结点可用如下结构体描述: typedef struct student { int num; 学号 int score; 成绩 struct student *next; 下一结点的首地址 } STU; typedef : 自定义类型符(见11.10)
■单向链表的建立 ①输入一个学生的数据。 ②分配结点空间,数据存入。 ③将该结点的首地址赋给上一结点的next,若该结点是第一个结点,则赋给头指针。 ④将该结点的next置为空,表示该结点为当前的最后结点。 学号 成绩 NULL head 学号 成绩 next 学号 成绩 next 学号 成绩 next 学号 成绩 next
STU *creat() { STU st,*p0=NULL,*p,*head=NULL; while(1) { scanf("%d%d",&st.num,&st.score); if(st.num<0) break; p=malloc(sizeof(STU)); *p=st; (*p).next=NULL; if(p0==NULL) head=p;p0为前一结点的指针 else (*p0).next=p; p0=p; } return head; } 学号 成绩 next 学号 成绩 NULL head 学号 成绩 next
■单向链表的访问 以输出为例 ①通过头指针找到第一个结点. ②输出当前结点的内容,并通过next找到后继结点,┄┄,直到next为空.
void output(STU *head) { STU *p=head; while(p) { printf("\n %d %d",(*p).num,(*p).score); p=(*p).next; } } 学号 成绩 next 学号 成绩 next 学号 成绩 NULL head 学号 成绩 next
■删除结点操作 ①按链表的访问方法找到相应结点。 ②若该结点是第一个结点,则将后继结点指针赋给头指针。 若该结点是最后一个结点,则将前缀结点的next置为空。 若该结点是中间结点,则将后继结点指针赋给前缀结点的next。 ③释放该结点所占的内存单元。 head 学号 成绩 next 学号 成绩 next 学号 成绩 NULL
假定要删除某一指定学号的结点 STU *delete(STU *head,int number) { STU *p =head,*p0=NULL; while(p) { if((*p).num==number) { if(p==head) head=(*p).next; else if((*p).next==NULL) (*p0).next=NULL; else (*p0).next=(*p).next; free(p); break; }else {p0=p; p=(*p).next;} } return head; }
head 学号 成绩 next 学号 成绩 next 学号 成绩 NULL 学号 成绩 next ■插入操作 假定将结点p 插入到结点p0的后面, 则插入操作的关键为: p->next=p0->next; p0->next=p;