????????学生管理系统怎么实现?首先要对问题能分析出框架来。这样在之后书写功能时就会对所需要的东西有一个清晰的认知。
? ? ? ? 那么,管理系统的任务就是:能删除、查找和修改学生信息,能进行排序、能打印信息。
????????这些都是最基本的功能,把这些功能框架写在一个c源文件里,当作主函数的运行区。
#include"StudentManager.h"
enum Option
{
EXIT,
ADD,
DEL,
SEARCH,
MODIFY,
SORT,
PRINT
};
int main()
{
int input = 0;
//创建人员名单
Student stulist;//人员名单
//初始化人员名单
InitStudent(&stulist);
do
{
menu();
printf("请选择:\n");
scanf_s("%d", &input);
switch(input)
{
case ADD:
AddStudent(&stulist);//修改一定传地址
break;
case DEL:
DelStudent(&stulist);
break;
case SEARCH:
SearchStudent(&stulist);//可以传值,但传地址效率更高
break;
case MODIFY:
ModifyStudent(&stulist);
break;
case SORT:
SortStudent(&stulist);
break;
case PRINT:
PrintStulist(&stulist);
break;
case EXIT:
printf("退出管理系统\n");
default:
break;
}
} while (input);
SaveFile(&stulist);
}
=========================================================================?
????????而后,是单独的一个文件,实现对所使用函数的声明,以便让业务逻辑更加分明:(同时尽量在这里就把所需要的存储类型定义了,这样写功能函数的时候会更加的清晰)
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//结构体定义
typedef struct StuInfo
{
//(1)每个学生的个人信息包括:姓名、学号、籍贯、年龄、数学、英语、物理成绩
char name[15];
char address[20];
char ID[15];
int age;
int score[3];
int sum;
int order;
}StuInfo;
//对结构体进行封装
typedef struct Student
{
StuInfo data[100]; //存放的人员信息
int size; //记录当前的人数
}Student;
int FindByName(Student* pc, char name[]);
void InitStudent(Student* pc);
void AddStudent(Student* pc);
void DelStudent(Student* pc);
void SearchStudent(Student* pc);
void ModifyStudent(Student* pc);
void SortStudent(Student* pc);
void SaveFile(Student* pc);
void PrintStulist(const Student* pc);
void menu();
?=========================================================================
????????之后,就是对函数功能的实现了,在实现功能之前,我们已经确定自己写的学生信息存储方式是什么。所以,就是函数功能的实现。
首先最基础的是菜单界面:
//显示菜单界面
void menu()
{
printf("---------------\n");
printf("1.增 2.删 \n");
printf("3.查 4.改 \n");
printf("5.排 6.打印 \n");
printf(" 0.退出 \n");
printf("---------------\n");
}
=========================================================================?
? ? 然后写一个函数来初始化空间(不多解释):
//初始化数据,以便使用
void InitStudent(Student* pc)
{
pc->size = 0;
memset(pc->data, 0, sizeof(pc->data));//初始化所有数据为0
}
????????之后就是一些功能的实现,比如我们先实现一个打印的功能,因为打印功能的代码在其他功能里也能使用和体现(例如查找):
? ? ? ? 指针pc想要解引用地址,就需要使用' -> ',其他的则是' . '。所以打印信息的操作就一目了然了,首先指针解引用取单个学生信息,然后结构体再去取出相应的信息数据来。
//打印信息
void PrintStulist(const Student* pc)
{
int i = 0;
//打印标题
printf("%-10s\t%-10s\t%-10s\t%-10s\t%-20s\n", "姓名", "学号", "籍贯", "年龄", "成绩:数学、英语、物理、");
for (i = 0; i < pc->size; i++)
{
printf("%-10s\t%-10s\t%-10s\t%-10d\t%d\t%d\t%d\n",
pc->data[i].name,
pc->data[i].ID,
pc->data[i].address,
pc->data[i].age,
pc->data[i].score[0], pc->data[i].score[1], pc->data[i].score[2]);//输入的三科成绩
}
}
? ? ? ? 在这里,需要简单的说明一下这个函数,使用const只读属性是因为我们接受的是学生名单的信息,但为了保证学生名单信息不会被误操作(除打印以外的操作),所以加上了const。那么可能有人会问,既然害怕被修改,为什么不直接定义一个普通变量接受学生信息,而要使用指针?那么请思考一下:指针接受的是地址,普通变量接受的是全部的信息,哪个效率更高呢?当然是传址的效率高。
=========================================================================
? ? ? ? 而后是添加的功能,一个新学生来到了班级,得重新录入信息吧,所以添加的功能需要一些什么?当然就是对应的信息去做修改(此处定义的学生名单进行封装时,定义的是100,当然,你也可以选择#define宏定义,以便后期维护):
//增加人员信息
void AddStudent(Student* pc)
{
if (pc->size == 100)
{
printf("名单已满,无法添加\n");
return;
}
//增加信息
printf("请输入名字:\n");
scanf_s("%s", pc->data[pc->size].name, 15);
printf("请输入学号:\n");
scanf_s("%s", pc->data[pc->size].ID, 15);
printf("请输入籍贯:\n");
scanf_s("%s", pc->data[pc->size].address, 20);
printf("请输入年龄:\n");
scanf_s("%d", &(pc->data[pc->size].age));
printf("请输入数学、英语、物理成绩:\n");
scanf_s("%d %d %d", &(pc->data[pc->size].score[0]), &(pc->data[pc->size].score[1]), &(pc->data[pc->size].score[2]));
pc->size++;
printf("信息添加成功\n");
}
? ? ? ? 添加的操作跟打印还是很相像的,就不多做解释。
=========================================================================
????????那么,看看剩下的几个功能,其中有修改、删除和查找,我们仔细分析一下这三个功能,都需要定向的找到某个学生,所以,我们先写一个查找的功能出来(查找只能顺序查找,一个一个去看哪个符合条件,所以用for或者while):
//定义一个查找的功能,以便删除,查找使用
int FindByName(Student* pc, char name[])
{
int i = 0;
for (i = 0; i < pc->size; i++)
{
if ((strcmp(pc->data[i].name, name)) == 0)
{
return i;
}
}
return -1;
}
? ? ? ? 这里查找使用的是姓名查找,你也可以去更改查找的功能实现更高级一点的操作,比如根据成绩找人,根据ID找人(本质都一样)。
=========================================================================
? ? ? ? 根据这个查找的功能,我们就可以去实现删除和修改操作了,当一个学生犯下滔天大罪,那就得退学咯,所以接下来实现一下删除:
? ? ? ? 这里要注意,因为是对学生的信息进行了更改,所以必须传地址。
//删除人员信息
void DelStudent(Student* pc)
{
char name[15] = { 0 };
if (pc->size == 0)
{
printf("名单已空,没有信息可以删除\n");
return;
}
//1.查找要删除的人
printf("请输入要删除的人:\n");
scanf_s("%s", name, 15);
int position = FindByName(pc, name);
/*
有/没有 --> 2. 删除
*/
if (position == -1)
{
printf("要删除的人不存在");
return;
}
//删除
int i = 0;
for (i = position; i < pc->size - 1; i++)//size-1是因为左后一个元素没必要被覆盖,后面代码删除
{
pc->data[i] = pc->data[i + 1];//依次覆盖实现向前移动和删除
}
pc->size--;
printf("删除成功\n");
}
? ? ? ? 删除的实现也算是相当简单了,因为实际上就等于是找到这个学生,然后把整个表格向前移动,直接覆盖这个学生就行(没办法,顺序表的缺陷就是删除操作时间复杂度高),同时,注意书写时也要判断学生存不存在。
? ? ? ? 那么查找操作呢?我们上面写的关于查找的功能,只是程序找到了这个人的信息,真正需要去查找,总得去查看一下这个人的信息吧。班里学生想知道自己的信息,找老师去查,肯定是需要输出信息的,所以这里也摘下了打印的一部分代码:
//查找指定的人员 --> 借用了删除人员的代码以及打印信息的代码
void SearchStudent(Student* pc)
{
char name[15] = { 0 };
printf("请输入要查找的人:\n");
scanf_s("%s", name, 15);
int position = FindByName(pc, name);
if (position == -1)
{
printf("要查找的人不存在");
return;
}//跟删除的判断一样的代码
else//去打印找代码,注意更改一些内容
{
printf("%-10s\t%-10s\t%-10s\t%-10s\t%-20s\n", "姓名", "学号", "籍贯", "年龄", "成绩:数学、英语、物理、");
printf("%-10s\t%-10s\t%-10s\t%-10d\t%d\t%d\t%d\n",
pc->data[position].name,
pc->data[position].ID,
pc->data[position].address,
pc->data[position].age,
pc->data[position].score[0], pc->data[position].score[1], pc->data[position].score[2]);//从打印那里摘来的代码
}
}
? ? ? ? 所以,基本上只要理解了最前面几个代码,剩下的也就迎刃而解了。那么我们还剩下跟查找有关的修改功能:
//修改指定的人员信息 --> 借用了查找的代码以及添加的代码
void ModifyStudent(Student* pc)//使用查找的代码进行改造
{
char name[15] = { 0 };
printf("请输入要修改的人:\n");
scanf_s("%s", name, 15);
int position = FindByName(pc, name);
if (position == -1)
{
printf("要修改的人不存在");
return;
}//跟删除的判断一样的代码
else//注意,这里是跟添加一样的代码
{
printf("请输入名字:\n");
scanf_s("%s", pc->data[position].name, 15);
printf("请输入学号:\n");
scanf_s("%s", pc->data[position].ID, 15);
printf("请输入籍贯:\n");
scanf_s("%s", pc->data[position].address, 20);
printf("请输入年龄:\n");
scanf_s("%d", &(pc->data[position].age));
printf("请输入数学、英语、物理成绩:\n");
scanf_s("%d %d %d", &(pc->data[position].score[0]), &(pc->data[position].score[1]), &(pc->data[position].score[2]));
printf("信息修改成功\n");
}
}
? ? ? ? 发现了吗?跟添加信息又有点像。总之,跟查找有关的函数功能基本上全实现了,那么还剩下排序的功能没有实现,由于排序的功能与查找关系就不大了,所以列为下一个大块。
=========================================================================
? ? ? ? 排序,我们能用到的排序其实有很多算法,比如选择排序、冒泡排序、插入排序、快速排序等等,这里就使用了一个更容易理解的插入排序算法来实现对学生的排序(算总分来排序):
//对人员进行排序
void SortStudent(Student* pc)
{
for (int i = 0; i < pc->size; i++)
{
pc->data[i].sum = pc->data[i].score[0] + pc->data[i].score[1] + pc->data[i].score[2];
}
printf("总分排序结果:\n");//使用插入排序实现此功能
StuInfo p;
int i, j;
for (i = 1; i < pc->size; i++)
if (pc->data[i].sum > pc->data[i - 1].sum)
{
p = pc->data[i];
for (j = i - 1; j >= 0 && pc->data[j].sum < p.sum; j--)
pc->data[j + 1] = pc->data[j];
pc->data[j + 1] = p;
}
StuInfo* t;
i = 0;
for (t = pc->data; t < pc->data + pc->size; t++)
{
pc->data[i].order = i + 1;
i++;
}
//把排序之后的名单打印出来
printf("%-10s\t%-10s\t%-10s\t%-10s\t%-20s\n", "姓名", "学号", "籍贯", "年龄", "成绩:数学、英语、物理、");
for (int i = 0; i < pc->size; i++)
{
printf("%-10s\t%-10s\t%-10s\t%-10d\t%d\t%d\t%d\n",
pc->data[i].name,
pc->data[i].ID,
pc->data[i].address,
pc->data[i].age,
pc->data[i].score[0], pc->data[i].score[1], pc->data[i].score[2]);//输入的三科成绩
}
}
? ? ? ? 那么,这段代码就只稍微解释一下所使用的排序的算法思路。插入排序,就是找到最大的那个值(得循环一遍才能找到),然后放到前面去,循环下来,就得到了一个顺序。具体的算法,等日后笔者再写一篇CSDN讲解一下吧,毕竟排序的内容还是很多的。
????????同时,这个函数也使用了打印的代码,毕竟,排完序得让别人看见嘛,所以也打印一下整个学生名单的信息。
? ? ? ? 写到这里,所有的功能基本就已经实现了。但是鉴于学生的信息最终可能要保存到文件里,以便日后使用,所以,这里又补了一个简单的文件保存的函数,当然,如果读者想实现读文件来写管理系统,那也是可以的:
//存储文件
void SaveFile(Student* pc)
{
FILE* fp = NULL;
fopen_s(&fp, "studentInfo.txt", "wt+"); //b表示以二进制方式打开文件
if (fp == NULL) //打开文件失败,返回错误信息
{
printf("open file for write error\n");
}
int i = 0;
for (i = 0; i < pc->size; i++) {
fprintf(fp, "%-10s\t%-10s\t%-10s\t%-10d\t 三科成绩:%d\t%d\t%d\n",
pc->data[i].name,
pc->data[i].ID,
pc->data[i].address,
pc->data[i].age,
pc->data[i].score[0], pc->data[i].score[1], pc->data[i].score[2]);
}
fclose(fp);
fp = NULL;
}
? ? ? ? 终于,我们的学生管理系统就算时搭建完成了,那么,小火罐们快拿着代码去试一试吧!
|