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,边上加上注释的可读性高。

②反思

功能还有许多不足之处。如申请的内存空间只在最后释放,而在删除函数里也许也能够及时释放的。另外,在姓名搜索中,没有考虑同名情况。输入函数里,虽然设置了很多判断条件来限制输入,提高容错率,但其实还能更进一步。

程序太过臃肿,一定还有很多可以简化或者合并的地方,一定要好好学习算法和数据结构方面的知识,无止境地追求优化。

菜单界面和提示信息在首次滚动显示时的效果应该不错,但操作次数多后,看上去就令人厌烦了。还可继续改进,使界面更加美观。

③感悟

代码一定要多敲,才会熟能生巧。如在写查找的子函数时,写第一个姓名查找还小心翼翼,到写学号查找、排名查找时就“火力全开”了。新知识或者陌生的知识,一定要多多了解它们的作用和使用场景,不用害怕使用它们,敢于“第一次”,当你迈出这一步,使用它们已经停不下来。

计算机只认识数字,所以它是最严谨的。在我们调试程序时,一定要有清晰的思路,不能瞎改乱查。往往写完程序只是任务的一小布,调试才是最花时间的重头戏。

像这样一个简单的成绩管理系统,在完成它的过程中,其实是很有获得感的,敲上去几行代码,就能得到一个想要的结果。想象未来学会更多有趣、“高端”的函数,语言,应该能创造出更让人眼前一亮的成果吧!

继续加油。