年底搞个基于C语言的运动会学生志愿者管理系统
目录
博主介绍
🍇背景
1、基本掌握面向过程程序设计的基本思路和方法;
2、达到熟练掌握C语言的基本知识和技能;
3、能够利用所学的基本知识和技能,解决简单的程序设计问题;
4、独立进行程序的编写,为后续学习及课程做好准备。
🍇环境
本程序开发所使用的环境:
-
Visual Studio 2010
-
Community版
-
Windows10 Professional操作系统
🍇要求
为了迎接某市运动会,请你学习C语言中相关知识,帮助组委会设计一个运动会志愿者信息管理系统,包含以下主要功能(不限于以下):
-
实现对志愿者信息的管理,包括添加志愿者信息(姓名,学号,联系方式等等),查询志愿者信息,删除志愿者信息;
-
实现运动会场馆的管理,包括添加场馆信息,查询场馆信息,删除场馆信息;
-
实现通过文件保存志愿者信息;
-
实现每个场馆对应不同志愿者的分配管理(例如,田径场需要4位志愿者,射击馆需要2位志愿者);
-
实现志愿者和场馆信息的快速查询统计功能,可以根据最后志愿服务时间评选最美志愿者;
-
除以上基本功能外,可根据各位同学自己设定的需求自行添加。
typedef struct sfield{ //场馆信息结构体
int fieldId; //场馆节点唯一数字id(int)
char fieldName[30]; //场馆名字(char[30])
char startTime[20]; //开始时间(char[20])
int lastTime; //持续时间(int)
int maxVolunteer; //需要志愿者数量(int)
int joinedVolunteer[MAX1]; //保存已加入志愿者信息(int[MAX1]) (MAX1=100)
struct sfield* head; //节点头部指针,指向上一个节点(struct sfield*)
struct sfield* next; //节点尾部指针,指向下一个节点(struct sfield*)
}field;
typedef struct svolunteer{ //志愿者信息结构体
int volunteerId; //志愿者节点唯一数字id(int)
char volunteerName[30]; //志愿者名字(char[30])
char idNum[20]; //志愿者学号(char[20])
char phoneNum[20]; //志愿者手机号(char[20])
int joinedField[MAX2]; //保存志愿者加入场馆唯一id(int[MAX2]) (MAX2=20)
int serviceTime; //志愿服务时长(int)
struct svolunteer* head; //节点头部指针,指向上一个节点(struct svolunteer*)
struct svolunteer* next; //节点尾部指针,指向下一个节点(struct svolunteer*)
}volunteer;
本程序使用两类结构体,分别储存志愿者信息(Volunteer)和场馆信息(Field)。每个结构体准备了指向上一个节点与下一个节点的指针,用于创建动态链表。如果要自行修改为使用数组储存数据,则可以不用这些指针。
void startUp();//初始化函数,将数据链表头结点头尾部初始化,数据部分清零,防止出错
int readFile(); //读取已保存的数据文件,若不存在则建立
int saveFile(); //储存已保存的数据文件,若不存在则建立
void newField();
//新增场馆信息
void newVolunteer(); //新增志愿者信息
void changeField(field*); //修改场馆信息
void changeVolunteer(); //修改志愿者信息
void deleteField(); //删除场馆信息
void deleteVolunteer(); //删除志愿者信息
void bind(); //注册志愿活动
void unbind(); //注销志愿活动
void showMenu(); //显示主菜单
void manageField(); //显示场地管理菜单
void manageVolunteer(); //显示志愿者管理菜单
field* selectFieldById(int); //通过唯一ID查找场馆指针
field* selectFieldByName(char*); //通过场馆名字查找场馆指针
volunteer* selectVolunteerById(int); //通过唯一ID查找志愿者指针
volunteer* selectVolunteerByName(char*); //通过志愿者名字查找志愿者指针
volunteer* selectVolunteerByIdNum(char*); //通过志愿者学号查找志愿者指针
volunteer* selectVolunteerByPhoneNum(char*); //通过志愿者手机号查找志愿者指针
void showField(field*); //显示指定场馆信息
void showVolunteer(volunteer*); //显示指定志愿者信息
void showAllField(); //显示所有场馆信息
void showAllVolunteer(); //显示所有志愿者信息
void findOutTopVolunteer(); //根据志愿服务时长寻找“最美志愿者”
void filePrintMenu(); //显示文件格式化输出菜单
void outPutFile(char*, int); //显示文件格式化输出菜单
🍇函数模块功能介绍
1、场馆信息管理
进入场馆信息管理模块,显示该模块下可用的操作:新增、修改、删除场馆信息、根据场馆名查询指定场馆信息。
-
newField()
函数,并且显示引导提示菜单,引导用户按格式输入要新增的场馆信息,本程序采用整行输入方式,用指定符号分割不同数据类型,最后将读入的输入字符串通过该指定符号进行分割,分割出几组字符串。首先检查字符串数量是否满足输入要求,如果有错误则会报错并退出该函数。若输入格式正确,下一步会调用selectFieldByName(char*)函数
寻找是否已有相同名字场馆信息,若返回非NULL值(
即确实已经存在同名场馆),则提示用户该错误并退出本函数。上述检查完成后,通过一开始声明的全局变量 _FIELD
找到储存场馆信息链表头结点,归递寻找至链表尾部,同时记录已有的节点数,最后在链表尾部分配新节点并连接上原链表尾,同时新节点的last指针
记录原链表尾节点地址。调用saveFile()函数
,将已有的数据保存至磁盘,最后提示用户保存成功结果。 -
修改场馆信息:先根据用户输入的场馆名,调用
selectFieldByName(char*)函数
,若返回空值则提示用户未找到对应名字的链表,否则根据该函数返回的field*类型指针
,调用changeField(field*)函数
。调用此函数后,会先显示该场馆信息节点中记录的场馆信息,并提示用户输入要修改的数据类型。用户输入后,先检查输入是否有误,有误则报错并返回,无误则提示输入要修改成的数据,输入后第输入数据进行检查,无误则将该节点信息对应数据修改为用户输入,调用saveFile()函数
保存文件,最后提示修改成功。 -
删除场馆信息:先根据用户输入的场馆名,调用
selectFieldByName(char*)函数
,若返回空值则提示用户未找到对应名字的链表,否则根据该函数返回的field*类型指针
,调用deleteField(field*)函数
。调用该函数后,会先显示该场馆信息节点中记录的场馆信息,并再次向用户确认是否要删除该场馆信息,用户输入否则退出此函数。若确认删除,先根据该节点的next指针
判断此节点是否为链表尾,若非节点尾,则根据该节点的head指针
找到上一个节点,分别将head指针
对应的上一个节点中的next指针
指向该要删除节点的 next指针指向的下一个节点,将next指针指向的下一个节点中的head指针
指向该要删除的节点的head指针
指向的上一个节点的地址,接着遍历该节点中的joinedVolunteer数组
,寻找对应的已加入志愿者,找到后进入该志愿者节点,将其joinedField数组
中对应的此要删除节点的id
记录删除掉,并将该志愿者的志愿服务时长减去该要删除节点的服务时间长度。待以上操作全部完成后,使用free(void*)函数
释放掉该要删除节点内存,然后调用saveFile()函数
将修改后的链表重新保存至文件。最后向用户返回删除成功提示。 -
查询指定场馆信息:提示用户输入要查询的场馆名,然后
调用 selectFieldByName(char*)函数
,若返回空值,则提示未找到对应场馆信息,退出函数。若返回非空值,则根据返回的field*类型指针
,调用showField(field*)函数
,打印出对应场馆信息,并遍历该场馆节点中的joinedVolunteer数组
,通过其中记录的志愿者id
,调用selectVolunteerById(int)
3、志愿服务管理
该模块没有对应独立二级菜单,仅有的两个功能(注册志愿活动
、注销志愿活动
)直接显示在主菜单,可以直接从主菜单
进入。
注册志愿活动:提示用户分别输入要注册的志愿活动对应的场馆信息和志愿者信息,与新增场馆/志愿者信息类似,这里采用读入整行字符串,通过特殊字符串来分割这一整行字符串的方式读取用户输入数据,首先检查输入格式是否有误,确认无误后根据输入数据分别寻找对应的场馆与志愿者节点,此处在输入志愿者姓名后还要求输入学号,作为二次校验,防止输入错误导致的绑定错误,如果输入的姓名与学号不对应同一个志愿者信息节点,则返回错误提示。一切输入数据确认无误后,进行绑定,具体流程如下:
-
在寻找到的输入场馆节点中的
joinedVolunteer数组
中进行查找,如果其中已存在对应输入的志愿者id,则提示不能重复绑定同一人并退出此绑定流程。 -
在寻找到的输入场馆节点检测已加入志愿者是否已达到最大允许加入志愿者数量,若已达到则提示该活动名额已满并退出此绑定流程。
-
在寻找到的输入志愿者节点中检测其已加入场馆数量是否已达到单个志愿者允许加入的最大场馆数量上限,若是则提示该志愿者志愿活动已达到上限。
-
以上检查均没有错误后显示对应场馆信息以及志愿者信息,并向用户确认是否要注册该志愿活动,若否则退出注册流程。
-
在寻找到的输入场馆节点中的
joinedVolunteer数组
中添加进寻找到的输入志愿者id
,在寻找到的输入志愿者节点中的joinedField数组
中添加寻找到的输入场馆id,并在寻找到的输入志愿者节点中的serviceTime(服务时间)
加上对应场馆志愿活动时间。最后向用户返回注册成功的提示。
4、寻找最美志愿者
调用 findOutTopVolunteer()函数
。先设置几个变量供寻找时临时使用,如设置 longestTime
为0
,然后遍历志愿者信息链表(头节点:全局变量 _VOLUNTEERS
),遍历到的节点如果 serviceTime
与 longestTime
相同,则向临时的 volunteer*型指针数组
的第一个无数据位添加进该节点的地址。然后继续向下遍历,如果遍历到的节点 serviceTime
大于 longestTime
,则将该节点 serviceTime值
赋给 longestTime变量
,并用 memset(void*,int,int)函数
将原有的 volunteer*型指针数组
清空,然后将其第一个数据位加入本节点的地址。最后遍历完成之后,最终打印出 volunteer*型指针数组
中非空成员数量,记为“最美志愿者”个数,然后遍历该 volunteer*型指针数组
,分别将节点对应成员信息打印出来作为“最美志愿者”信息,以及临时变量 longestTime
作为“最美志愿者”的志愿服务时长也打印出来。
5、文件的存取
本程序只会在启动时首先自动执行的 startUp()函数
中调用 readFile()函数对已保存文件进行读取,若数据文件不存在则建立数据文件然后退出 readFile()函数。先分别建立 field型
与 volunteer型
变量用以储存临时数据,打开“fieldData.txt”文件
,使用 fread()函数按块读入已储存的数据内容,并将其存入 field型临时变量中,然后分配 field大小内存作为节点将该变量的值分别赋入节点中对应变量,并将上一个节点的 next指针
指向本新分配节点,将本新分配节点的head指针
指向上一个节点,将 next指针
设为NULL。循环 fread()函数以及后面这一组操作,直到读取到文件尾,最后关闭文件。然后对 volunteer型数据
进行相同的操作,此处类比。
在每一次新增、修改、删除操作(不论是对场馆信息还是志愿者信息)以及注册、注销志愿活动之后,都会调用 saveFile()函数
对数据进行保存。若数据文件存在也会使用“w”模式将数据全部重新覆写,接着类似 readFile()
,先建立临时变量,根据全局变量 _FIELD找到场馆信息链表头,然后遍历场馆信息链表,将每个节点中的数据部分(即除 head、next指针外的部分)拷贝到临时变量,然后调用 fwrite()函数
将数据写入文件,直至链表遍历完毕,最后关闭文件。然后对 volunteer型数据进行相同的操作,此处类比。
本程序虽然没有使用“wb”类的二进制数据储存法,但是使用的 fwrite()函数是不会对非字符类型变量进行转换的,所以生成的数据文件虽以文本形式储存,但是其对应非char类型的数据位部分会显示乱码,而且字符串\0后未填满部分也会以“烫”字储存(因为VS编译器对于字符串中空字符默认的特性),所以本程序直接生成的数据文件并不具备有良好的可读性,为了解决这一问题,本程序额外加入了“格式化输出文件”功能(自主添加的额外功能),将数据以人类可阅读的形式格式化输出。
💫点击直接资料领取💫
这里有各种学习资料还有有有趣好玩的编程项目,更有难寻的各种资源。
❤️关注微信苏州程序大白公众号❤️
- 点赞
- 收藏
- 关注作者
评论(0)