1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > 第九章 使用结构体类型处理组合数据——用户自定义数据类型

第九章 使用结构体类型处理组合数据——用户自定义数据类型

时间:2022-07-11 00:21:11

相关推荐

第九章 使用结构体类型处理组合数据——用户自定义数据类型

9.1定义和使用结构体变量

例9.1 把一个学生的信息放在一个结构体变量中,然后输出这个学生的信息。

程序代码如下:

#include <stdio.h>int main(){struct student{int num;char name[20];char sex;char addr[20];}student1={10101,"Li Lin",'M',"123 Beijing Road"};printf("NO.: %d\nname: %s\nsex: %c\naddress: %s\n",student1.num,student1.name,student1.sex,student1.addr);return 0;}

运行结果如下:

引用结构体变量应遵守以下规则:

(1)可以引用结构体变量中成员的值,引用方式为:结构体变量名.成员名

“.”是成员运算符,它在所有的运算符中优先级最高,因此可以把studentl.num作为一个整体来看待。上面赋值语句的作用是将整数10010赋给student1变量中的成员num。

(2)如果成员本身又属一个结构体类型,则要用若干个成员运算符,一级一级地找到最低的一级的成员。只能对最低级的成员进行赋值或存取以及运算。

student1.num(结构体变量student1中的成员num)

student1.birthday.month (结构体变量student1中的成员birthday中的成员month)

(3)对结构体变量的成员可以像普通变量一样进行各种运算(根据其类型决定可以进行的运算)。

student2.score=student1.score;(赋值运算)

sum=student1.score+student2.score;(加法运算)

student1.age++;(自加运算)

由于“."运算符的优先级最高,因此studentl.age++是对studentl.age进行自加运算,而不是先对age进行自加运算。

(4)同类的结构体变量可以互相赋值

student1=student2;//假设student1和student2已定义为同类型的结构体变量

(5)可以引用结构体变量成员的地址,也可以引用结构体变量的地址。

scanf ("&d", &student1.num);(输入student1.num的值)

printf("&o", &student1);(输出结构体变量student1的首地址)

但不能用以下语句整体读入结构体变量,例如:scanf ("d, s, c, d, f, s", &student1);

结构体变量的地址主要用作函数参数,传递结构体变量的地址。

例9.2 输人两个学生的学号、姓名和成绩,输出成绩较高的学生的学号、姓名和成绩。

程序代码如下:

#include <stdio.h>int main(){struct student{int num;char name[20];float score;}student1,student2;scanf("%d%s%f",&student1.num,student1.name,&student1.score);scanf("%d%s%f",&student2.num,student2.name,&student2.score);//student1.name前面没有&,因为name是数组名,本身就代表地址printf("The higher score is:\n");if(student1.score>student2.score)printf("%d %s %6.2f\n",student1.num,student1.name,student1.score);else if(student1.score<student2.score)printf("%d %s %6.2f\n",student2.num,student2.name,student2.score);else{printf("%d %s %6.2f\n",student1.num,student1.name,student1.score);printf("%d %s %6.2f\n",student2.num,student2.name,student2.score);}return 0;}

运行结果如下:

9.2结构体数组

例9.3 有3个候选人,每个选民只能投票选一人,要求编写一个统计选票的程序,先后输入被选人的名字,最后输出各人得票结果。

程序代码如下:

#include <string.h>#include <stdio.h>struct person//声明结构体类型struct person{char name[20];//候选人姓名int count;//候选人得票数}leader[3]={"Li",0,"Zhang",0,"Fun",0};//定义结构体数组并初始化int 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++;//如果输人的姓名和某一元素中的name成员相同,就给该元素的count成员加1 }printf("\nResult:\n");for(i=0;i<3;i++)printf("%5s:%d\n",leader[i].name,leader[i].count);//输出数组所有元素中的信息return 0;}

运行结果如下:

1.定义结构体数组一般形式是:

(1) struct结构体名

{成员表列} 数组名[数组长度];

(2)也可以先声明一个结构体类型(如struct person),然后再用此类型定义结构体数组:

结构体类型 数组名[数组长度];

如:struct person leader[3];//leader是结构体数组名

2.对结构体数组初始化的形式是在定义数组的后面加上

={初值表列};

如:struct person leader[3]= {"Li",0, "Zhang",0, "Fun", 0};

例9.4 有N个学生的信息(包括学号、姓名、成绩),要求按照成绩的高低顺序输出各学生的信息。

程序代码如下:

#include <stdio.h>#define N 5struct student{int num;char name[20];float score;};int main(){struct student stu[5]={{10101,"zhang",78},{10103,"wang",98.5},{10106,"li",86},{10108,"ling",73.5},{10110,"fun",100}};struct student temp;//定义结构体变量temp,用做交换时的临时变量int i,j,k;printf("The order is:\n");for(i=0;i<N-1;i++){k=i;for(j=i+1;j<N;j++)if(stu[j].score>stu[k].score)//进行成绩比较k=j;temp=stu[k];stu[k]=stu[i];stu[i]=temp;//stu[k]与stu[i]元素整体互换}for(i=0;i<N;i++)printf("%6d %8s %6.2f\n",stu[i].num,stu[i].name,stu[i].score);printf("\n");return 0;}

运行结果如下:

9.3结构体指针

所谓结构体指针就是指向结构体数据的指针,一个结构体变量的起始地址就是这个结构体变量的指针。

如果把一个结构体变量的起始地址存放在一个指针变量中,那么,这个指针变量就指向该结构体变量。指针变量既可以指向结构体变量,也可以用来指向结构体数组中的元素。

指针变量的基类型必须与结构体变量的类型相同。

例如:

struct student *pt;//pt可以指向struct student类型的数据

例9.5 通过指向结构体变量的指针变量输出结构体变量中成员的信息。

程序代码如下:

#include <stdio.h>#include <string.h>int main(){struct student{long num;char name[20];char sex;float score;};struct student stu_1;//定义struct student类型的变量stu_1struct student *p;//定义指向struct student类型数据的指针变量pp=&stu_1;//p指向结构体变量stu_1stu_1.num=10101;strcpy(stu_1.name,"Li Lin");//对结构体变量的name成员赋值stu_1.sex='M';stu_1.score=89.5;printf("No.: %ld\nname: %s\nsex: %c\nscore: %5.1f\n",stu_1.num,stu_1.name,stu_1.sex,stu_1.score);//输出各成员的值printf("\nNo.: %d\nname: %s\nsex: %c\nscore: %5.1f\n",(*p).num,(*p).name,(*p).sex,(*p).score);//输出各成员的值return 0;}

运行结果如下:

(*p).num是p指向的结构体变量中的成员num。注意*p两侧的括号不可省,因为成员运算符“."优先于“*"运算符,*p.num就等价于* (p.num)了。

说明:为了使用方便和使之直观,C语言允许把p->num代替(* p).num,它表示p所指向的结构体变量中的num成员。同样,p->name等价于(*p).name。

"->"称为指向运算符。

如果p指向一个结构体变量,以下3种形式等价:

①结构体变量.成员名

②(*p).成员名

③p->成员名

指向结构体变量的指针变量,也可以用来指向结构体数组元素。

例9.6 有3个学生的信息,放在结构体数组中,要求输出全部学生的信息

程序代码如下:

#include <stdio.h>struct student{int num;char name[20];char sex;int age;};struct student stu[3]={{10101,"Li Lin",'M',18},{10102,"Zhang Fun",'M',19},{10104,"Wang Min",'F',20}};//定义结构体数组并初始化int main(){struct student *p;//定义指向struct student 结构体变量的指针printf(" No. Namesex age\n");for(p=stu;p<stu+3;p++)printf("%5d %-20s %2c %4d\n",p->num,p->name,p->sex,p->age);return 0;}

运行结果如下:

9.4用结构体变量和结构体变量的指针作为函数参数

将一个结构体变量的值传递给另一个函数,有3个方法:

(1)用结构体变量的成员作参数。例如,用stu[1].num或stu[2].name作函数实参,将实参值传给形参。用法和用普通变量作实参是一样的,属于“值传递”方式。应当注意实参与形参的类型保持一致。

(2)用结构体变量作实参。用结构体变量作实参时,采取的也是“值传递”的方式,将结构体变量所占的内存单元的内容全部顺序传递给形参,形参也必须是同类型的结构体变量。在函数调用期间形参也要占用内存单元。这种传递方式在空间和时间上开销较大,如果结构体的规模很大时,开销是很可观的。此外,由于采用值传递方式,如果在执行被调用函数期间改变了形参(也是结构体变量)的值,该值不能返回主调函数,这往往造成使用上的不便。因此一般较少用这种方法。

(3)用指向结构体变量(或数组)的指针作实参,将结构体变量(或数组)的地址传给形参。

例9.7 有N个结构体变量stu,内含学生学号、姓名和3门课程的成绩。要求输出平均成绩最高的学生的信息(包括学号、姓名、3门课程成绩和平均成绩)。

解题思路:

按照功能函数化的思想,分别用3个函数来实现不同的功能:

(1)用input函数来输入数据和求各学生平均成绩。

(2)用max函数来找平均成绩最高的学生。

(3)用print函数来输出成绩最高学生的信息。

在主函数中先后调用这3个函数,用指向结构体变量的指针作实参,最后得到结果。

程序代码如下:

#include <stdio.h>#define N 3//学生数为3struct student{int num;char name[20];float score[3];//三门课成绩float aver;//平均成绩};int main(){system("chcp 65001");//加上这句,中文就不会变成乱码void input(struct student stu[]);//函数声明struct student max(struct student stu[]);//函数声明void print(struct student stu);//函数声明struct student stu[N], *p=stu;//定义结构体数组和指针input(p);//调用input函数 print(max(p));//调用print函数,以max函数的返回值作为实参return 0;}void input(struct student stu[])//定义input函数{int i;printf("请输入各学生的信息:学号、姓名、三门课成绩:\n");for(i=0;i<N;i++){scanf("%d %s %f %f %f",&stu[i].num,stu[i].name,&stu[i].score[0],&stu[i].score[1],&stu[i].score[2]);//输入数据stu[i].aver=(stu[i].score[0]+stu[i].score[1]+stu[i].score[2])/3.0;//求各人平均成绩}}struct student max(struct student stu[]){int i, m=0;//用m存放成绩最高的学生在数组中的序号for(i=0;i<N;i++)if(stu[i].aver>stu[m].aver) m=i;//找出平均成绩最高的学生在数组中的序号return stu[m];//返回包含该生信息的结构体元素}void print(struct student stud){printf("\n成绩最高的学生是:\n");printf("学号:%d\n姓名:%s\n三门成绩:%5.1f,%5.1f,%5.1f\n平均成绩:%6.2f\n",stud.num,stud.name,stud.score[0],stud.score[1],stud.score[2],stud.aver);}

运行结果如下:

以上3个函数的调用,情况各不相同:

调用input函数时,实参是指针变量p,形参是结构体数组名,传递的是结构体元素的地址,函数无返回值,作用是给stu数组各元素赋予确定的值。input 函数的形参是struct student类型的数组stu。用input函数时,将主函数中的stu数组的首元素的地址传给形参数组stu,使形参数组stu与主函数中的stu数组具有相同的地址。因此在input函数中向形参数组stu输入数据就等于向主函数中的stu数组输入数据。

调用max函数时,实参是指针变量p,形参是结构体数组名,传递的是结构体元素的地址,函数的返回值是结构体类型数据。stu[m]是一个结构体数组的元素,max函数的类型为struct student类型。

调用print函数时,实参是结构体变量(结构体数组元素),形参是结构体变量,传递的是结构体变量中各成员的值,函数无返回值。print函数的形参是stud是structstudent类型的变量。

9.5 用指针处理链表

结构体变量的一个重要的用途是和指针相结合,构造线性链表。线性链表就是动态地进行存储分配的一种数据结构。

链表中的各元素称为“结点”,每个结点都应包括两个部分:用户需要用的实际数据和下一个结点的地址。直到最后一个结点,该结点不再指向其他结点,它称为“表尾”,它的地址部分放一个“NULL”(表示“空地址”),链表到此结束,可以看到链表中各结点在内存中的地址可以是不连续的。

链表这种数据结构,必须利用结构体变量和指针变量才能实现。在一个结点中包含两个部分:数据部分和一个指针变量(该指针变量存放下一结点的起始地址)

structstudent

{int num;

floatscore;

structstudent *next;//next成员是指针变量

};

next是指针类型的成员。为了构造链表,应当把next定义为指向structstudent类型数据的指针变量。这时,next既是struct student类型中的一个成员,又指向structstudent类型的数据,见图9.9。

例9.8 建立一个如图9.9所示的简单链表,它由3个学生数据的结点组成。输出各结点中的数据。

程序代码如下:

#include <stdio.h>#define NULL 0struct student{int num;float score;struct student *next;};int main(){struct student a,b,c,*head,*p;//定义3个结构体变量作为链表的结点a.num=10101;a.score=89.5;//对结点a的num和score成员赋值b.num=10103;b.score=90;//对结点b的num和score成员赋值c.num=10107;c.score=85;//对结点c的num和score成员赋值head=&a;//将结点a的起始地址赋给头指针heada.next=&b;//将结点b的起始地址赋给a结点的next成员b.next=&c;//将结点c的起始地址赋给a结点的next成员c.next=NULL;//c结点的next成员不存放其他结点地址p=head;//使p也指向a结点do{printf("%ld %5.1f\n",p->num,p->score);//输出p指向的结点的数据p=p->next;//使p指向下一个结点}while(p!=NULL);//输出完c结点后p的值为NULL,循环终止return 0;}

运行结果如下:

建立动态链表是指在程序执行过程中从无到有地建立起一个链表,即一个一个地开辟结点和输人各结点数据,并建立起前后相连的关系。

例9.9 建立一个有两名学生学号和成绩数据的单向动态链表。

解题思路:

(1)定义结构体变量,其成员包括学号、成绩和指针变量。

(2)动态地开辟一个新单元(动态开辟内存单元用malloc函数)。使指针变量p和head指向此结点。

(3)向此结点输入数据。

(4)再开辟第2个新结点,并使指针变量p指向此结点。

(5)使第2个结点中的指针变量的值为NULL,即不指向任何对象,链表到此为止。

(6)输出两个结点中的数据。

程序代码如下:

#include <stdio.h>#include <malloc.h>//用malloc函数开辟新单元时需用此头文件#define LEN sizeof(struct student)//LEN代表struct student类型数据的字节数struct student{int num;float score;struct student *next;//指针变量成员};int main(){struct student *head, *p;//定义指针变量head和p//建立链表head=p=(struct student * ) malloc(LEN);//开辟一个新单元,并让p和head指向它scanf("%d,%f",&p->num,&p->score);//输入第一个结点的数据p=(struct student *) malloc(LEN);//开辟第二个新单元,并让p指向它scanf("%d,%f",&p->num,&p->score);//输入第二个结点数据head->next=p;//使第1个结点中的next成员指向第2个结点p->next=NULL;//使第2个结点中的next成员不指向任何对象// 输出两结点中的数据p=head;//使p指向第一个结点printf("\n结点 1:%d,%6.2f\n",p->num,p->score);p=p->next;//使p指向第二个结点printf("结点 2:%d,%6.2f\n",p->num,p->score);return 0;}

运行结果如下:

共用体类型:

使几个不同类型的变量共占同一段内存的结构,称为“共用体"类型的结构。

定义共用体类型变量的一般形式为:

union共用体名

{成员表列

}变量表列;

结构体变量所占内存长度是各成员占的内存长度之和。每个成员分别占有其自己的内存单元。而共用体变量所占的内存长度等于最长的成员的长度。

枚举类型:

如果一个变量只有几种可能的值,则可以定义为枚举类型。所谓“枚举”是指将变量的值一一列举出来,变量的值只限于列举出来的值的范围内。声明枚举类型用enum开头。

enumweekday {sun, mon, tue, wed, thu, fri, sat};

workday=mon;

week_end=sun;

enum {sun, mon, tue, wed, thu, fri, sat}workday, week_end;//直接定义枚举变量

中sun, mon,,sat称为枚举元素枚举常量。它们是用户定义的标识符。

workday和week_end被定义为枚举变量,它们的值只能是sun到sat之一。

例9.10 口袋中有红、黄、蓝、白、黑5种颜色的球若干个。每次从口袋中先后取出3个球,问得到3种不同色的球的可能排列,输出每种排列的情况。

解题思路:球只能是5种色之一,而且要判断各球是否同色,应该用枚举类型变量处理。

设取出的球为i、j、k。根据题意,i、j、k分别是5种色球之一,并要求i≠j≠k。可以用穷举法,即把每一种排列都试一下,看哪一组符合条件。

算法可用图9.12表示。用n累计得到3种不同色球的次数。外循环使第1个球i从red变到black。中循环使第2个球j也从red变到black。如果i和j同色则不可取,只有i和j不同色(i≠j)时才需要继续找第3个球,此时第3个球k也有5种可能(red到black),但要求第3个球不能与第1个球或第2个球同色,即k≠i,k≠j。满足此条件就得到3种不同色的球。输出这种3色组合方案,然后使n加1。外循环全部执行完后,全部方案就已输出完了。最后输出总数n。

下面的问题是如何实现图9.12中的“输出一种取法”,可以采用图9.13的方法。

为了输出3个球的颜色,显然应经过3次循环,第1次输出i的颜色,第2次输出j的颜色,第3次输出k的颜色。在3次循环中先后将i、j、k赋予pri。然后根据pri的值输出颜色信息。在第1次循环时,pri的值为i,如果i的值为red,则输出字符串"red”,其余类推。

程序代码如下:

#include <stdio.h>int main(){enum color {red,yellow,blue,white,black};//声明枚举类型colorenum color i,j,k,pri; int n,loop;//定义枚举变量n=0;for(i=red;i<=black;i++)//逐个检查是否符合条件for(j=red;j<=black;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){case red: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("\ntotal:%5d\n",n);return 0;}

运行结果如下:

······

枚举类型是把可能的值全部一一列出,枚举变量的值只能是其中之一。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。