890行。设计最强最全学生成绩管理系统(C语言大作业)(文后附解析说明的博客)
上周太忙,拖更了一期博客,这里先给大家赔个不是哈...
并再说明一下,大学进入期末月啦,笔者得专心复习,所以这个月更完这两期就不更了,等放寒假以后再和大家分享。
话不多说,进入正题,相信很多计算机专业的同学在这临近期末的时候,都遇见了类似的计算机大作业——设计信息系统。笔者收到的大作业具体要求如下:
不同学校的要求不尽相同,这里分享的是笔者的设计思想。
当然,如上要求对于不同的人来说难易感受也不同,不过尽可能严格要求自己吧,设计你能设计出的最好的系统,来证明给自己看——这学期真的收获很多很多。
这篇博客我将按照作业形式来写,可能稍显简略。详细的如何思考分析,实现过程,注意事项,知识分享都将在下一篇博客提到。
说明:在《C Prime Plus》中有提到编写C程序的7个步骤,从前往后分别是:定义程序的目标,设计程序,编写代码,编译,运行程序,测试和调试程序,维护和修改代码,说明。并指出许多初学者经常忽略第一步和第二步,直接开始编写代码,以致像编写这种”庞大”的大作业程序时,会写出一些非常难找的隐藏错误,跳过前期规划,往往找bug上花费了更多的时间。“因为他们写出的程序难看、缺乏条理、让人难以理解”。个人觉得十分在理,所以在作业的“结果分析”前加上了“功能的设计与分析”
正文:
四、功能的设计与分析
1、结合内容①⑦,皆为录入信息,但为给后续增加信息留够空间,所以开始可以定义一个很大的数组。但静态定义一个“用不完”的数组实在浪费空间,不如在堆上动态申请内存空间(malloc,calloc...),现学现用,用最好的。同时注意到录入的信息包括姓名、成绩。结合实际情况,成绩可分为多门科目的成绩,而信息还可添加性别,学号,总分,排名等,用好结构体,这些都可实现。
2、发现②其实被后面的多个功能包含,如排序、查找、输出总分大于指定分数的同学信息等,所以后续结合其他功能考虑。
3、③④皆为查找,结合我们将录入学号,还可通过学号查找。设计程序时可以考虑遍历结构体中的指定信息,与目标对比,一旦找到printf输出。
4、⑤排序,可降序就可升序,这也是可以添加的功能,这里想到学过的选择,冒泡排序。但提高对自己的要求,考虑用实现最复杂,但时间复杂度最小的快速排序实现。
5、⑥删除,开始想用数组存储信息,遍历找到需要删除的学生(数组元素),将其后的所有元素前移一位,覆盖掉需要删除的位置,达成目的。但这样做时间复杂度太高,所以考虑用新学的链表来实现。
6、⑧划定分数线,即遍历+判断语句,当判断总分比指定分数大时,printf输出。
7、关注实验里的额外要求,菜单调用。即printf出一个菜单,显示出各个选项,当输入选项时,可以采用switch语句,调用各个case中的具体函数中去。
8、增加功能。结合实际情况,一个成绩管理系统难免出现录入信息出错的地方,所以考虑增加修改信息的功能。而录入一次考试所有学生的信息后,大家往往会关注考试结果(不只是排名),这就需要统计功能,考虑增加统计平均分和各科考试情况的功能。
9、当使用者还未录入便选择查询、删除等操作时,程序肯定就不知道该怎么做了,所以还可以增加一些判断条件,解决不正确输入问题,提高程序容错率。
五、具体代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<string.h>
#include<windows.h>
int flag = 0, numstudent = 0, numcourse, choice; //宏定义,变量声明
int* pc;
typedef struct information //结构体定义,每一个都代表着一个学生
{
long int ID;//学号
char name[20];//姓名
char sex[10];//性别(男/女)
int score[12];//各科分数
int total = 0;//总分
}information;
typedef struct node //链表嵌套调用“信息”结构体
{
information data;
node* next;
}node;
node* t, * head;
//主要功能函数 //函数声明
int Input();//选项输入
void Enter();//信息录入
void Revise();//修改
void Delete();//删除
void Find();//查找
void Statistic();//统计
void Sort();//排序
void Total_Level();//划线查看
//辅助函数
void Menu();//菜单
void Tips();//提示功能函数
void Tips();//智能提示
int Legal_Check();//合法性检查
void Header1();//输入表头
void Header2();//输出表头
void Header3();//统计表头
void Swap();//交换
void Down_Sort();//降序排列
void Up_Sort();//升序排列
void Find_by_name();//姓名查找
void Find_by_ID();//学号查找
void Find_by_rank();//排名查找
void Find_by_total();//总分查找
void Play_On_Play();//“我为自己找事干”函数
void Course_Average();//统计平均分
void Tips(char a[])
{
int i;
for (i = 0; i < strlen(a); i++)
{
printf("%c", a[i]);
Sleep(30);//利用休眠函数,使说明语句一个一个字打印,让程序有温度!
}
}
void Menu(int way)
{
int i;
char tip[] = "\n请选择菜单中你想要进行的操作选项:\n\n";
char menu[15][54] = { " ------------------------------------------------\n",
" ----------- 菜单 -----------\n",
" |1、录入学生信息 |\n",
" |2、添加学生信息 |\n",
" |3、修改学生信息 |\n",
" |4、删除学生信息 |\n",
" |5、查找学生信息 |\n",
" |6、查看成绩统计结果 |\n",
" |7、学生成绩排名 |\n",
" |8、划定分数线并查看 |\n",
" |0、退出 |\n",
" ----------- -----------\n",
" ------------------------------------------------\n",
"\nTo:" };
if (way == 1)//第一次打印菜单,缓慢打印
{
Tips(tip);
Sleep(1000);
for (i = 0; i < 15; i++)
{
printf("%s", menu[i]);
Sleep(150);
}
}
else//后续菜单直接显示,不拖沓
{
for (i = 0; i < 15; i++)
printf("%s", menu[i]);
}
return;
}
void Header1()//只录入ID、姓名、性别、和各科成绩,所以增加相应表头
{
int i;
for (i = 0; i < 20 + 8 * numcourse; i++)
printf("-");
printf("\n");
printf("%-7s %-7s %-7s", "ID", "姓名", "性别");
for (i = 1; i <= numcourse; i++)
{
printf("%-s%-4d", "课程", i);
}
printf("\n");
for (i = 0; i < 20 + 8 * numcourse; i++)
printf("-");
printf("\n");
}
void Header2()//输出表头将增加自动计算出的总分和排名
{
int i;
for (i = 0; i < 35 + 8 * numcourse; i++)
printf("-");
printf("\n");
printf("%-7s %-7s %-7s", "I D", "姓名", "性别");
for (i = 1; i <= numcourse; i++)
{
printf("%-s%-4d", "课程", i);
}
printf("%-7s %-7s", "总分", "排名");
printf("\n");
for (i = 0; i < 35 + 8 * numcourse; i++)
printf("-");
printf("\n");
}
void Header3()//在统计中,显示平均分时用到
{
int i;
printf(" |");
for (i = 1; i <= numcourse; i++)
{
printf("%-s%-4d", "课程", i);
}
printf("\n");
for (i = 0; i < 10 + 8 * numcourse; i++)
printf("-");
printf("\n");
printf("平均分 |");
}
void Play_One_Play() //“玩一玩”函数
{ //尝试用\b退格来做到“网页加载中...”的经典效果^_^
char a[] = "...";
int i, j;
printf("统计中");
for (j = 0; j < 3; j++)
{
for (i = 0; i < strlen(a); i++)
{
printf("%c", a[i]); Sleep(350);
}
printf("\b\b\b");
}
system("cls");
}
int Input()
{
int i, j, z;
char warning1[] = "\a未录入任何信息,不能进行该操作哦!\n";
char warning2[] = "\a没有这项操作哦!\n";
char warning3[] = "你已经录入过信息,再次录入将覆盖原存档哦,确定继续吗? (1----是/0----否)\n";
char warning4[] = "库里没有任何人的信息!\n";
char choose_again[] = "请重新选择";
char enter_again[] = "即将进行重新录入";
do
{
if (flag == 0)
Menu(1);
else
{
Menu(2);
}
scanf("%d", &choice); //多个判断条件解决不正确输入问题
if (flag == 0 && choice >= 2 && choice <= 8)//卡住首次录入前输入其他选项
{
Tips(warning1);
Tips(choose_again);
Sleep(1000);
system("cls");
}
else if (choice < 0 || choice >8)//卡住选不存在的选项
{
Tips(warning2);
Tips(choose_again);
Sleep(1000);
system("cls");
}
else if (numstudent == 0 && flag == 1)//卡住删除所有信息后进行任何选项
{
Tips(warning4);
Sleep(1000);
Tips(enter_again);
Sleep(1000);
system("cls");
choice = 1;
return 1;
}
else if (flag == 1 && choice == 1)//卡住二次录入
{
Tips(warning3);
scanf("%d", &z);
if (z == 1)
{
free(t);
free(pc);
return choice;
}
else
{
Tips(choose_again);
Sleep(1000);
system("cls");
}
}
} while ((flag == 0 && choice >= 2 && choice <= 8) || (choice < 0 || choice >8) || (flag == 1 && choice == 1));
flag = 1;
system("cls");
return choice;
}
void Enter() //思来想去,既然删除,增加都将用到链表,便用链表“一条龙”服务好了。
{
int i, j, ns;
node* p, * q;
information a;
char tip1[] = "\n请输入需要录入信息的学生人数:";
char tip2[] = " 本次测验的课程数量:";
char warning[] = "未找到合适存放信息的空间,数据可能出错!";
char tip3[] = "\n请输入需要添加信息的学生人数:";
if (choice == 1)//将使用链表,所以区分开首次录入和后续添加
{
Tips(tip1);
scanf("%d", &ns);
numstudent += ns;
Tips(tip2);
scanf("%d", &numcourse);
printf("\n");
head = NULL;
q = NULL;
pc = (int*)calloc(numcourse, sizeof(int));//在堆上申请numcourse个int大小的内存空间。用多少,要多少,不浪费。
if (pc == NULL)//每次申请动态内存空间,都严谨判断一下是否申请成功
{
Tips(warning);
printf("\a");
}
}
else//后续添加
{
Tips(tip3);
scanf("%d", &ns);
numstudent += ns;
q = head;
while (q->next != NULL)
q = q->next;
}
Header1();//输入用·表头,后续为输入内容
for (i = 0; i < ns; i++)
{
a.total = 0;
scanf("%ld %s %s", &a.ID, a.name, a.sex);
for (j = 0; j < numcourse; j++)
{
scanf("%d", &a.score[j]);//循环输入该生各科成绩,同时用指针pc记录各科的总成绩,方便后续统计平均分。
pc[j] += a.score[j];
a.total += a.score[j];
}
p = (node*)malloc(sizeof(node));//输一个申请一个,不滥用内存。
if (p == NULL) //判断是否申请成功
{
Tips(warning);
printf("\a");
}
p->data = a;
p->next = NULL;
if (head == NULL)
head = p;
else
q->next = p;
q = p;
}
printf("\n");
for (i = 0; i < 20 + 8 * numcourse; i++)//格式美观
{
printf("-");
Sleep(20);
}
printf("\n 录入成功!\n\n");//提示录入成功
Sleep(750);
}
void Swap(node* a, node* b)//快排函数将调用它
{
information temp = a->data;
a->data = b->data;
b->data = temp;
}
void Up_Sort(node* left, node* right)//升序用·快排
{
if (left == NULL || left->next == NULL || left == right)
return;
information temp = left->data;
node* i = left->next;
node* j = left->next;
node* pre_i = left;
while (j != right->next)//特色快排,两个哨兵同时从一段开始移动,便于理解。
{
if (j->data.total < temp.total)
{
Swap(i, j);
pre_i = i;
i = i->next;
}
j = j->next;
}
Swap(left, pre_i);
Up_Sort(left, pre_i);
Up_Sort(i, right);
}
void Down_Sort(node* left, node* right)//降序用·快排
{
if (left == NULL || left->next == NULL || left == right)
return;
information temp = left->data;
node* i = left->next;
node* j = left->next;
node* pre_i = left;
while (j != right->next)
{
if (j->data.total > temp.total)
{
Swap(i, j);
pre_i = i;
i = i->next;
}
j = j->next;
}
Swap(left, pre_i);
Down_Sort(left, pre_i);
Down_Sort(i, right);
}
void Sort()
{
int i, j, way;
char tip[] = "\n请选择查看成绩排名的方式(1----降序/0----升序):";
node* ph = head;
while (ph->next != NULL)//因为链表没法知道多长,所以简单用遍历寻找一下“尾”
ph = ph->next;
Tips(tip);
scanf("%d", &way);
Header2();
t = head;
if (way == 0)//至于为什么升序、降序分成不同的快排,因为链表的每一个节点中存的next指针只指向后一个,并没有存储 //
Up_Sort(head, ph); //前一个节点,所以不能只排一个顺序然后从后往前遍历。(个人理解)
else
Down_Sort(head, ph);
for (i = 0; t != NULL; i++)//排序完显示成绩排名
{
printf("%-7ld %-7s %-7s", t->data.ID, t->data.name, t->data.sex);
for (j = 0; j < numcourse; j++)
{
printf(" %-7d", t->data.score[j]);
}
printf("%-7d ", t->data.total);
printf("%-7d\n", (way == 0) ? numstudent - i : i + 1);
t = t->next;
}
for (i = 0; i < 35 + 8 * numcourse; i++)//美观用的打印
printf("-");
printf("\n");
}
void Revise()
{
int i, j, pass, g;
char tip1[] = "请输入你想修改信息的学生姓名:";
char tip2[] = "请输入该学生修改后的新信息↓\n";
char warning[] = "\a查无此人!\n";
char tip3[] = "修改完成!\n";
char tip4[] = "是否要继续修改?(是----1/否----0)";
char name[20];
A: //后文用goto函数判断是否需要继续修改
i = 0;
node* ph = head;
while (ph->next != NULL)
ph = ph->next;
Up_Sort(head, ph);
system("cls");
Tips(tip1);
scanf("%s", name);
t = head;
while (t != NULL)//指针遍历搜索需要修改信息的人
{
if (!strcmp(t->data.name, name))
{
Header2();
printf("%-7ld %-7s %-7s", t->data.ID, t->data.name, t->data.sex);
pass = t->data.total;
for (j = 0; j < numcourse; j++)
{
printf(" %-7d", t->data.score[j]);
pc[j] -= t->data.score[j];
t->data.total -= t->data.score[j];
}
printf("%-7d %-7d", pass, numstudent - i);
printf(" (原信息)\n\n");
Sleep(1000);
Tips(tip2);
Sleep(500);
Header1();
scanf("%ld %s %s", &t->data.ID, t->data.name, t->data.sex);
for (j = 0; j < numcourse; j++)
{
scanf("%d", &t->data.score[j]);
pc[j] += t->data.score[j];
t->data.total += t->data.score[j];
}
Tips(tip3);
Tips(tip4);
scanf("%d", &g);
if (g == 1)
goto A;
else
return;
}
i++;
t = t->next;
}
Tips(warning);//如果遍历没找到,有所提示
Tips(tip4);
scanf("%d", &g);
if (g == 1)
goto A;
}
void Delete()
{
int j, flag, g;
char tip1[] = "请输入你想要删除信息的学生姓名:";
char warning[] = "\a查无此人!\n";
char tip2[] = "删除成功!\n";
char tip3[] = "是否继续删除?(是----1/否----0)";
char tip4[] = "信息库已空空如也。\n";
char name[20];
A:
flag = 0;
system("cls");
Tips(tip1);
scanf("%s", name);
t = head;
if (numstudent == 1)//特判只剩一个人的情况,因为再删,头指针就指向NULL了,情况稍微复杂
{
if (!strcmp(t->data.name, name))
{
for (j = 0; j < numcourse; j++)
pc[j] -= t->data.score[j];
head = t->next;
flag = 1;
}
}
while (t->next != NULL)
{
if (!strcmp(t->data.name, name))//删除时特判是删除第一个人的情况
{
for (j = 0; j < numcourse; j++)
pc[j] -= t->data.score[j];
head = t->next;
flag = 1;
break;
}
else if (!strcmp(t->next->data.name, name))//删除非第一个人的情况
{
for (j = 0; j < numcourse; j++)
pc[j] -= t->next->data.score[j];
t->next = t->next->next;
flag = 1;
break;
}
t = t->next;
}
if (flag == 1) //判断是否继续删除,如果是,goto回去。
{
Tips(tip2);
numstudent--;
Sleep(500);
if (numstudent > 0)
{
Tips(tip3);
scanf("%d", &g);
if (g == 1)
goto A;
}
else
Tips(tip4);
}
else
Tips(warning);
}
void Find_by_name()//通过名字查找学生信息(不考虑同名)
{
int i = 0, j;
char tip1[] = "请输入你想要查找的学生名字:";
char tip2[] = "确有此人!\n\n";
char warning[] = "\a查无此人!\n";
char a[20];
Tips(tip1);
scanf("%s", a);
t = head;
while (t != NULL)//遍历查询名字
{
if (!strcmp(t->data.name, a))
{
Tips(tip2);
Sleep(500);
Header2();
printf("%-7ld %-7s %-7s", t->data.ID, t->data.name, t->data.sex);
for (j = 0; j < numcourse; j++)
{
printf(" %-7d", t->data.score[j]);
}
printf("%-7d %-7d\n", t->data.total, numstudent - i);
return;
}
i++;
t = t->next;
}
Tips(warning);//遍历到底,提示没找到。
}
void Find_by_ID()//通过学号查找学生信息
{
int i = 0, j;
long int a;
char tip1[] = "请输入你想查找的学生学号:";
char tip2[] = "确有此人!\n\n";
char warning[] = "\a查无此人!\n";
Tips(tip1);
scanf("%ld", &a);
t = head;
while (t != NULL)
{
if (t->data.ID == a)
{
Tips(tip2);
Sleep(500);
Header2();
printf("%-7ld %-7s %-7s", t->data.ID, t->data.name, t->data.sex);
for (j = 0; j < numcourse; j++)
{
printf(" %-7d", t->data.score[j]);
}
printf("%-7d %-7d\n", t->data.total, numstudent - i);
return;
}
i++;
t = t->next;
}
Tips(warning);
}
void Find_by_rank()//通过排名寻找学生信息
{
int i = numstudent, j, rank;
char tip1[] = "请输入你想要查询的学生成绩排名:";
char warning[] = "\n\a都没有这么多人哦!\n";
char tip2[] = "\n请重新选择!\nTo:";
char tip3[] = "找到了!\n";
do
{
Tips(tip1);
scanf("%d", &rank);
if (rank > numstudent)
{
Tips(warning);
Tips(tip2);
Sleep(500);
system("cls");
}
} while (rank > numstudent);
t = head;
while (t != NULL)
{
if (i == rank)
{
Tips(tip3);
Sleep(500);
Header2();
printf("%-7ld %-7s %-7s", t->data.ID, t->data.name, t->data.sex);
for (j = 0; j < numcourse; j++)
{
printf(" %-7d", t->data.score[j]);
}
printf("%-7d %-7d\n", t->data.total, rank);
return;
}
i--;
t = t->next;
}
}
void Find_by_total()//通过总分查找学生信息
{
int i = 0, j, sum, count = 0;
char tip1[] = "请输入你想查找的学生总分:";
char tip2[] = "找到了!\n\n";
char warning[] = "\a查无此人!\n";
Tips(tip1);
scanf("%d", &sum);
t = head;
while (t != NULL)
{
if (t->data.total == sum)//找到后不返回继续找,考虑总分一样的同学
{
if (count == 0)
{
Tips(tip2);
Sleep(500);
Header2();
}
printf("%-7ld %-7s %-7s", t->data.ID, t->data.name, t->data.sex);
for (j = 0; j < numcourse; j++)
{
printf(" %-7d", t->data.score[j]);
}
printf("%-7d %-7d\n", t->data.total, numstudent - i);
count++;
}
i++;
t = t->next;
}
if (count == 0)
Tips(warning);
}
void Find()//查找的主要函数,选择不同的查找方式
{
int want, g;
char ask1[] = "你想通过什么方式进行查找呢?\n";
char tip[] = "(1----姓名/2----学号/3----排名/4----总分)\n\n";
char ask3[] = "\n\n是否继续查找?(是----1/否----0)\n";
char warning1[] = "\a没有该项查找!,查找失败!";
node* ph = head;
while (ph->next != NULL)
ph = ph->next;
Up_Sort(head, ph);
A:
system("cls");
Tips(ask1);
Tips(tip);
Sleep(750);
printf("To:");
scanf("%d", &want);
switch (want)
{
case 1:Find_by_name(); break;
case 2:Find_by_ID(); break;
case 3:Find_by_rank(); break;
case 4:Find_by_total(); break;
default:Tips(warning1); break;
}
Tips(ask3);
scanf("%d", &g);
if (g == 1)
goto A;
}
void Total_Level()//划分数线,遍历统计,并输出过线和未过线的学生
{
int i = 0, j;
int level, passcount = 0;
char tip[] = "请输入你想划定的分数线:";
char tip1[] = "\n\n以上为过线同学↑,";
char tip2[] = "\n\n以上为未过线同学↑,";
Tips(tip);
scanf("%d", &level);
Play_One_Play();
node* ph = head;
while (ph->next != NULL)
ph = ph->next;
Down_Sort(head, ph);
t = head;
while (t != NULL)
{
if (t->data.total >= level)
{
if (passcount == 0)
Header2();
printf("%-7ld %-7s %-7s", t->data.ID, t->data.name, t->data.sex);
for (j = 0; j < numcourse; j++)
{
printf(" %-7d", t->data.score[j]);
}
printf("%-7d %-7d\n", t->data.total, numstudent - i);
passcount++;
}
else
{
break;
}
t = t->next;
}
if (passcount == 0)
printf("\a无人过线!\n\n");
else
{
Tips(tip1);
printf("共%d人。\n", passcount);
}
Sleep(1000);
if (passcount != numstudent)
Header2();
while (t != NULL)
{
printf("%-7ld %-7s %-7s", t->data.ID, t->data.name, t->data.sex);
for (j = 0; j < numcourse; j++)
{
printf(" %-7d", t->data.score[j]);
}
printf("%-7d %-7d\n", t->data.total, numstudent - i);
t = t->next;
}
if (passcount != numstudent)
{
Tips(tip2);
printf("共%d人\n", numstudent - passcount);
}
else
printf("ALL PASS!\n\n");
}
void Course_Average()//计算各科平均分
{
int i, j;
char average[] = "\n各科平均分情况如下:\n\n";
Tips(average);
Sleep(250);
Header3();
for (i = 0; i < numcourse; i++)
printf(" %-7.1f", (float)pc[i] / numstudent);
printf("\n\n");
}
void Statistic()//统计的总函数,用等级划分统计各科情况
{
int i, j;
int count[5] = { 0 };
char tip[] = "下面将以如下等级统计各科情况↓\n\n";
Play_One_Play();
Course_Average();
Tips(tip);
Sleep(1000);
printf("A: 90~100\n"); Sleep(250);
printf("B: 80~89\n"); Sleep(250);
printf("C: 70~79\n"); Sleep(250);
printf("D: 60~69\n"); Sleep(250);
printf("E: 0~59\n\n"); Sleep(250);
Sleep(1000);
for (i = 0; i < 42; i++)
printf("-");
printf("\n A B C D E (人次)\n");
for (i = 0; i < 42; i++)
printf("-");
for (i = 0; i < numcourse; i++)
{
printf("\n课程%d", i + 1);
t = head;
while (t != NULL)
{
switch (t->data.score[i] / 10)
{
case 10:
case 9:count[0]++; break;
case 8:count[1]++; break;
case 7:count[2]++; break;
case 6:count[3]++; break;
default:count[4]++; break;
}
t = t->next;
}
for (j = 0; j < 5; j++)
{
printf(" %-4d", count[j]);
count[j] = 0;
}
}
}
int main()
{
printf(" ");
char welcome[] = "欢迎使用Upping编写的学生成绩管理系统!\n";
char end[] = "系统即将关闭,期待您的下次使用!\n";
char wish[] = "祝您生活愉快";
Tips(welcome);
Sleep(1000);
A:
switch (Input())//灵活运用system函数,使每一次执行完菜单命令之后,都能清屏,使界面美观
{
case 1:
Enter();
system("pause");
system("cls");
goto A;
case 2:
Enter();
system("pause");
system("cls");
goto A;
case 3:
Revise();
system("pause");
system("cls");
goto A;
case 4:
Delete();
system("pause");
system("cls");
goto A;
case 5:
Find();
system("pause");
system("cls");
goto A;
case 6:
Statistic();
system("pause");
system("cls");
goto A;
case 7:
Sort();
system("pause");
system("cls");
goto A;
case 8:
Total_Level();
system("pause");
system("cls");
goto A;
case 0: //当要退出程序时,归还动态申请存放信息的空间。
free(t);
free(pc);
printf(" ");
Tips(end);
Sleep(1000);
printf(" ");
Tips(wish);
Sleep(1000);
system("cls");
}
return 0;
}
六、实验结果及分析
1、实验运行过程及分析
①菜单运行:
分析:灵活地运用了睡眠函数(Sleep),使程序运行有了层次感,菜单清晰美观。
②录入
分析:通过system(“cls”)清屏,进入专门录入的界面,表头随着输入的课程数量自动变长,当信息之间以Tab隔开时,实现自动对齐,工整美观。
③添加
分析:因为采用链表,所以添加并不会覆盖首次录入的信息,并能够在后续的统计排序等功能上正常显示。
2、运行结果
①查找
分析:提供整整4种方式进行查找,功能多样化,查找准确,找不到人将发出警告。
②排序
分析:可升序可降序,使用快排,时间复杂度低。总分、排名,全部信息一目了然。
③删除+统计
我们先删除李华的信息(男 66 66 66),并增加另外两个人的信息
大佬(ID:66 男 100 100 100)
学渣(ID:55 男 2 2 2)
接着选择统计操作:
分析:通过在录入时用来记录每科总分的指针pc和学生人数,实现了统计平均分的功能,另外,用浮点数稍精确地得出了各科各分数段的情况,符合实际生活中的统计情形。
④退出
还有划分数线查看及删除功能的具体运行情况没有展示,但都完成了开始设计程序时设想的功能。
3、心得与体会
①收获
在敲代码前,有过很长时间的思考。不断地想怎样运用所学的知识去实现自己给程序定下的目标。当自己用静态数组存信息感到浪费而心生不满时,能勇于使用还在很陌生的动态分配空间函数(malloc,calloc),并在使用中渐渐熟练。
在写添加信息的函数时,速度变得非常缓慢,犹豫到底要不要使用链表,发现如果使用链表,一是涉及到结构体嵌套,二是包括开始录入、后续的排序、删除等等,全部的程序都要因此而变动。而且它将不再是简单的数组,多个指针不停变换,而指针又几乎是C语言中最让人头疼的工具。但在尽力啃下来以后,发现自己对指针、链表、结构体有了更深入的理解,不会害怕使用,而是乐于去使用。
写完程序后,也反反复复运行并试验各种功能,找bug。许多bug都是设置断点,一步步调出来的,特别是这种“大工程”,通过运行结果锁定出问题的子函数,再反推运算,找出问题。无形之中增强了自己debug的能力。
在定义函数时,我有意识地去查找该函数将要实现功能的对应英文,并结合短横线命名。我想这是个好习惯,增加程序可读性,应该继续保持(而不是用拼音shu_ru)。同时总结反思也很重要,如开始本来想用枚举型enum定义一下主函数Switch里的case,希望它更加易读,但因为规范使用枚举型的话全部字母都要大写,发现还不如就用case1,2,3,边上加上注释的可读性高。
②反思
功能还有许多不足之处。如申请的内存空间只在最后释放,而在删除函数里也许也能够及时释放的。另外,在姓名搜索中,没有考虑同名情况。输入函数里,虽然设置了很多判断条件来限制输入,提高容错率,但其实还能更进一步。
程序太过臃肿,一定还有很多可以简化或者合并的地方,一定要好好学习算法和数据结构方面的知识,无止境地追求优化。
菜单界面和提示信息在首次滚动显示时的效果应该不错,但操作次数多后,看上去就令人厌烦了。还可继续改进,使界面更加美观。
③感悟
代码一定要多敲,才会熟能生巧。如在写查找的子函数时,写第一个姓名查找还小心翼翼,到写学号查找、排名查找时就“火力全开”了。新知识或者陌生的知识,一定要多多了解它们的作用和使用场景,不用害怕使用它们,敢于“第一次”,当你迈出这一步,使用它们已经停不下来。
计算机只认识数字,所以它是最严谨的。在我们调试程序时,一定要有清晰的思路,不能瞎改乱查。往往写完程序只是任务的一小布,调试才是最花时间的重头戏。
像这样一个简单的成绩管理系统,在完成它的过程中,其实是很有获得感的,敲上去几行代码,就能得到一个想要的结果。想象未来学会更多有趣、“高端”的函数,语言,应该能创造出更让人眼前一亮的成果吧!
继续加油。