目录

前言 

 一、设计题目与要求

二、 设计软硬件环境

三、功能设计与描述

(1)登录界面

(2)菜单界面

(3)文件操作

(4)成绩输入

(5)成绩查找

(6)成绩修改

(7)成绩删除

(9)成绩排序和成绩输出

(10)密码修改

四、程序运行结果展示

五、设计总结

2021年7月25号补充:

2021年12月16日补充:

2022年4月16日补充:

六、源代码

前言 

                哈喽大家好!我又来写博客啦! 因为大一要结束了,我也要写课设了,我们老师其实给了十几个选择的课设内容,因为我太菜了,班级成绩管理系统也算是比较简单好学的一个了,所以我就选择了这个,肝了一周之后,我终于完成了这个系统,放松下来的我就打算把这个系统写成一篇博客,供更多人借鉴。我们老师要求写的报告是有模板的,课设报告我是按模板来写的,为了方便点,写这篇博客我也按照这个模板写好了。最后提醒下初学者:因为这个系统用到的库函数比较多,所以我不可能每一个都要讲清楚它的作用、头文件,所以就需要你们动动勤快的小手去百度啦!(我是这么过来的,这样真的很有用!)

补充

        补充一下,因为我在做这个课设的时候使用dev写的代码,然后就没考虑vs能不能运行,现在修改了一下代码,我用vs2019是可以正常运行的了(注意vs要关掉sdl检查:项目->属性->c/c++->sdl检查->将是改为否->应用并保存,否则scanf函数会报错)。

        然后如果还有问题的话可以私我,看到会回复的。

 一、设计题目与要求

简单概括需求:1、学生成绩录入

                         2、从高到低输出成绩排名(按总分)

                         3、查找学生成绩(按学号或姓名)

                         4、修改学生成绩

                         5、删除学生成绩

非必须需求:密码登录、修改密码、隐藏密码输入

二、 设计软硬件环境

开发环境为C语言环境,开发软件为Dev-C++,所用电脑为戴尔Windows11,没有用到数据库。

三、功能设计与描述

(1)登录界面

void enter(){//登录界面
	system("cls");//清屏 
	time_t t;
	struct tm *p;
	time(&t);
	p=gmtime(&t);
	printf("\t\t\t\t**********************************************\n");
	printf("\t\t\t\t**--------------%d年%02d月%02d日--------------**\n",1900+p->tm_year,1+p->tm_mon,p->tm_mday);
	printf("\t\t\t\t****************当前时间%02d时%02d分%***************\n",8+p->tm_hour,p->tm_min); 
	printf("\t\t\t\t**********************************************\n");
	printf("\t\t\t\t**                                          **\n");
	printf("\t\t\t\t**             学生成绩管理系统             **\n");
	printf("\t\t\t\t**                                          **\n");
	printf("\t\t\t\t**********************************************\n");
	printf("\t\t\t\t**     感谢使用本系统,希望您使用愉快!     **\n"); 
	printf("\t\t\t\t**********************************************\n");
	printf("\t\t\t\t**    制作者:小菜鸟    学号:1008965223    **\n"); 
	printf("\t\t\t\t**         班级:计算机类201班              **\n");
	printf("\t\t\t\t**      学院:计算机与电子信息学院          **\n");
	printf("\t\t\t\t**********************************************\n");
	printf("请输入密码(初始密码为:123456):\n");
	int sum2=0;
	while(1){
		hide_password(key,21);
		printf("\n");
		FILE *fpr=fopen("password.txt","r");
		fgets(password, 21, fpr);
		fclose(fpr);
		int n=strlen(key),m=strlen(password);
		if(n!=m){
			sum2++;
			if(sum2>=5){
				printf("多次输入密码错误!系统关闭!\n");
				sum2=0;
				exit(0);
			}
			printf("密码错误!请重新输入!你还有%d次机会!\n",5-sum2); 
		}else{
			int sum1=0;
			for(int i=0;i<n;i++){
				if(key[i]!=password[i]){
					sum1++;
				}
			}
			if(sum1==0){
				printf("登录成功!\n");
					break;
			}else{
				sum2++;
				if(sum2>=5){
					printf("多次输入密码错误!系统关闭!\n");
					sum2=0;
					exit(0);
				}
				printf("密码错误!请重新输入!你还有%d次机会!\n",5-sum2);
			}
		}
	}
}

用水平制表符(\t,也说跳格符)将界面移到中间,在开头定义全局字符串变量来设置初始密码123456(为字符串类型),通过利用getch()定义函数可以实现隐藏密码输入,用strlen()函数获取输入的密码长度进行循环判断密码是否正确。通过时间函数time()获取当前时间并进行转换输出年月日时分。

通过一个死循环while1),密码输入正确用break退出,连续输错5次后结束程序。

隐藏输入函数代码:

​
void hide_password(char *pswd, unsigned maxlen){//隐藏密码进行输入 
    int index = 0;
    char buff = '\0';

    while ((buff = getch()) != '\r'){
        if (buff == '\b' && index != 0){
            index--;
            printf("\b \b");
        } else if (index < maxlen - 1 && buff != '\b'){
            pswd[index++] = buff;
            putchar('*');
        }
    }
    pswd[index] = '\0';
}

​

(2)菜单界面

void welcome(){//主菜单界面 
	system("cls");//清屏 
	time_t t;
	struct tm *p;
	time(&t);
	p=gmtime(&t);
	printf("\t\t\t\t**********************************************\n");
	printf("\t\t\t\t**--------------%d年%02d月%02d日--------------**\n",1900+p->tm_year,1+p->tm_mon,p->tm_mday);
	printf("\t\t\t\t****************当前时间%02d时%02d分%***************\n",8+p->tm_hour,p->tm_min); 
	printf("\t\t\t\t**********************************************\n");
	printf("\t\t\t\t**                                          **\n");
	printf("\t\t\t\t**               欢迎进入!                 **\n");
	printf("\t\t\t\t**                                          **\n");
	printf("\t\t\t\t**********************************************\n");
	printf("\t\t\t\t**---------------功能菜单-------------------**\n");
	printf("\t\t\t\t**       增加学生信息 ------------1         **\n");
	printf("\t\t\t\t**       删除学生信息 ------------2         **\n");
	printf("\t\t\t\t**       修改学生信息 ------------3         **\n");
	printf("\t\t\t\t**       查询学生信息 ------------4         **\n");
	printf("\t\t\t\t**       输出成绩排名 ------------5         **\n");
	printf("\t\t\t\t**       修改登录密码 ------------6         **\n");
	printf("\t\t\t\t**       退出管理系统 ------------0         **\n");
	printf("\t\t\t\t**********************************************\n");
	printf("请输入你想要进行的操作(数字): ");
}

通过system()函数(头文件为#include<windows.h>)进行清屏。在主函数中,通过while1)的死循环,可以不断输入choice,进行功能的选择。用switch条件语句对choice的值进行判断,每个选择设置相应的函数。当不继续执行时用break语句跳出死循环或用exit0)语句(写在goodbye函数中)直接结束程序。

程序结束出口goodbye函数和打印爱心函数(程序结束时打印爱心):

void printflove(){//打印爱心 
	float a,x,y;
	for(y=1.5f; y>-1.5f; y-=0.1f){
		for(x=-1.5f; x<1.5f; x+=0.05f){
			a = x*x+y*y-1;
			char ch = a*a*a-x*x*y*y*y<=0.0f?'*':' '; 
			putchar(ch);  
		}
		printf("\n");
		printf("\t\t\t  ");
	}	 
}

void goodbye(){//结束程序 
	system("cls");
	printflove();
	printf("\t**********************************************\n");
	printf("\t\t\t\t**                                          **\n");
	printf("\t\t\t\t**        欢迎下次使用!~再见!~~           **\n");
	printf("\t\t\t\t**                                          **\n");
	printf("\t\t\t\t**********************************************\n");
	exit(0);
} 

主函数代码:

int main()//主函数 
{
	int choice=0;
	enter();
	readFile(&List);//进入程序后先读取文件,少了这句之前存的成绩都不能输出来 
	while(1){
			welcome();
			scanf("%d",&choice);
			switch (choice){
			case 1:
				new_student(&List);//增加学生信息 
				break; 
			case 2:
				delete_student(&List);//删除学生信息 
				break;
			case 3:
				modify_student(&List);//修改学生信息 
				break;
			case 4:
				search_student(&List);//查询学生信息 
				break;
			case 5:
				output_sort(&List);//输出成绩排名 
				break;
			case 6:
				change();//修改密码 
				break;
			case 0:
				goodbye();//结束程序
				break;
		}
		printf("是否需要继续操作?(是:1/否:0)\n");
		scanf("%d",&choice);
		if(choice==0){
			goodbye(); 
		}
	}
	return 0;
}

(3)文件操作

通过typedef将结构体定义为新的数据类型node,这样后面定义结构体变量或结构体指针变量是就可以直接用(node)或(node*)了

typedef struct Node{
	long long id;//学号
	char name[50];//姓名
	char sex[10];//性别
	int math,english,phisics,scatter,generation;//高等数学、大学英语、大学物理、离散数学、线性代数 
	int sum;//总分 
	
	struct Node *next;
}node;

node List;//链表 

fopen和fclose函数打开和关闭文件,用fscanf()函数读取文件中的学生信息,文件命名为:score.txt 。

int readFile(node *L){//读取文件 
	FILE *fpr=fopen("score.txt","r");
	node st;
	node *s;
	node *t=L; 
	if(fpr==NULL){
		return 0;
	}else{
		while(fscanf(fpr,"%lld %s %s %d %d %d %d %d %d",&st.id,st.name,st.sex,&st.math,&st.english,&st.phisics,&st.scatter,&st.generation,&st.sum)!=EOF){
			s=(node *)malloc(sizeof(node));
			*s=st;
			t->next=s;
			t=s;
			t->next=NULL;
			
		}
	}
	fclose(fpr);//关闭文件指针 
	return 1;
}

fprintf()函数将学生信息写入文件中,进行信息保存

int saveFile(node *L){//保存文件
	FILE *fpw=fopen("score.txt","w");
	if(fpw==NULL) return 0;
	node *p=L->next;
	while(p!=NULL){
		fprintf(fpw,"%lld %s %s %d %d %d %d %d %d\n",p->id,p->name,p->sex,p->math,p->english,p->phisics,p->scatter,p->generation,p->sum);
		p=p->next;
	}
	fclose(fpw);//关闭文件指针 
	return 1;
}

(4)成绩输入

定义一个结构体变量,用printf()语句和scanf()语句指引输入新的学生信息,另外,为了防止输入的学号或姓名重复,需要遍历链表查询是否有重复的学号或姓名。定义类型为(node*)型的函数searchid(按学号查询)和函数searchname(按姓名查询),用定义的结构体指针变量对链表进行遍历。

node * searchid(int id,node *L){//按学号查找 
	node *p=L;
	while(p->next!=NULL){//遍历链表 
		if(p->next->id==id){
			return p;
		}
		p=p->next;
		
	}
	return NULL;
}

node * searchname(char name[],node *L){//按姓名查找 
	node *p=L;
	while(p->next!=NULL){//遍历链表 
		if(strcmp(name,p->next->name)==0){
			return p;
		}
		p=p->next;
		
	}
	return NULL;
}

死循环来进行学号和姓名的输入,若输入的学号或是姓名不存在链表中,则用break语句退出死循环。否则要继续进行输入。

为了使成绩输入功能更加方便,可以不断的进行输入,我们再次利用死循环while(1),当不需要继续输入学生信息时输入0跳出死循环,这样就可以实现学生成绩的不断输入,同时用死循环对输入的数据进行限制。

void new_student(node *L){//增加学生信息 
	system("cls");
	node st;
	int choice=0;
	while(1){
		printf("请输入新增学生相关信息\n");
		printf("学号(10位数):");
		while(1){
			scanf("%lld",&st.id);
			node *p=searchid(st.id,L);
			if(p==NULL&&st.id>=1000000000&&st.id<=9999999999){
				break;
			}else{
				printf("该学生的学号已存在或长度不合理!请重新输入学号!\n");
			}
		}
		printf("姓名(中文最长不要超4个字):");
		while(1){
			scanf("%s",st.name);
			node *p=searchname(st.name,L);
			int len=strlen(st.name);
			if(p==NULL&&len<=10){
				break;
			}else{
				printf("该学生的姓名已存在或长度不合理!请重新输入姓名!\n");
			}
		}
		printf("性别(男|女):");
		while(1){
			scanf("%s",st.sex);
			if(strlen(st.sex)==2){
				break;
			}else{
				printf("输入不合理!(性别应为:男 / 女)请重新输入性别!\n");
			}
		}
		printf("高数成绩(0~100):");
		while(1){
			scanf("%d",&st.math);
			if(st.math>=0&&st.math<=100){
				break;
			}else{
				printf("输入的分数不合理!应在0~100之间!请重新输入高数成绩!\n");
			}
		}
		printf("英语成绩(0~100):");
		while(1){
			scanf("%d",&st.english);
			if(st.english>=0&&st.english<=100){
				break;
			}else{
				printf("输入的分数不合理!应在0~100之间!请重新输入英语成绩!\n");
			}
		}
		printf("大物成绩(0~100):"); 
		while(1){
			scanf("%d",&st.phisics);
			if(st.phisics>=0&&st.phisics<=100){
				break;
			}else{
				printf("输入的分数不合理!应在0~100之间!请重新输入大物成绩!\n");
			}
		}
		printf("离散成绩(0~100):");
		while(1){
			scanf("%d",&st.scatter);
			if(st.scatter>=0&&st.scatter<=100){
				break;
			}else{
				printf("输入的分数不合理!应在0~100之间!请重新输入离散成绩!\n");
			}
		}
		printf("线代成绩(0~100):");
		while(1){
			scanf("%d",&st.generation);
			if(st.generation>=0&&st.generation<=100){
				break;
			}else{
				printf("输入的分数不合理!应在0~100之间!请重新输入线代成绩!\n");
			}
		}
		st.sum=st.math+st.english+st.phisics+st.scatter+st.generation;
		insertlist(&List,st);
		printf("是否需要继续输入学生信息?(是:1 / 否:0)\n");
		scanf("%d",&choice);
		if(choice==0){
			break;
		}
	}
}

定义insertlist()函数将存入了学生成绩的结构体变量插入链表头部,并将链表重新保存进文件:

void insertlist(node *L,node e){//头插法,将学生信息插入到链表 
	node *h=L;
	node *s=(node *)malloc(sizeof(node));
	*s=e; 
	s->next=h->next;
	h->next=s;
	saveFile(L);
}

(5)成绩查找

chioce的值来选择按学号查询还是按姓名查询

运用之前定义的searchid函数和searchname函数进行查询。

若已经输入的学生成绩中找不到输入的学号或姓名,则输出“查无此人”。若找到了则进行输出。

void search_student(node *L){//查询学生信息 
	system("cls");
	int choice=0;
	long long id;
	char name[50];
	node *st;
	printf("按学号查询----- 1\n");
	printf("按姓名查询----- 2\n");
	printf("请输入查询方式:");
	scanf("%d",&choice);
	if(choice==1){
		printf("请输入要查询的学号(10位数):");
		scanf("%lld",&id);
		st=searchid(id,L); 
		if(st==NULL){
			printf("查无此人!\n");
		}else{
			st=st->next;
			printf("_________________________________________________________________________________\n");
			printf("|学号\t\t|姓名\t|性别\t|高数\t|英语\t|大物\t|离散\t|线代\t|总分\t|\n");
			printf("_________________________________________________________________________________\n");
			printf("|%lld\t|%s\t|%s\t|%d\t|%d\t|%d\t|%d\t|%d\t|%d\t|\n",st->id,st->name,st->sex,st->math,st->english,st->phisics,st->scatter,st->generation,st->sum);
			printf("_________________________________________________________________________________\n");
		}
	}else if(choice==2){
		printf("请输入要查询的姓名:");
		scanf("%s",name);
		st=searchname(name,L); 
		if(st==NULL){
			printf("查无此人!\n");
		}else{
			st=st->next;
			printf("_________________________________________________________________________________\n");
			printf("|学号\t\t|姓名\t|性别\t|高数\t|英语\t|大物\t|离散\t|线代\t|总分\t|\n");
			printf("_________________________________________________________________________________\n");
			printf("|%lld\t|%s\t|%s\t|%d\t|%d\t|%d\t|%d\t|%d\t|%d\t|\n",st->id,st->name,st->sex,st->math,st->english,st->phisics,st->scatter,st->generation,st->sum);
			printf("_________________________________________________________________________________\n");
		}
	}
}

(6)成绩修改

void modify_student(node *L){//修改学生信息 
	system("cls");
	int choice=-1;
	long long  id;
	printf("请输入要查找的学生学号(10位数):");
	scanf("%lld",&id);
	node *st=searchid(id,L);

	if(st==NULL){
		printf("查无此人!");
		return ;
	}
		st=st->next;
	while(1){
		system("cls");
		printf("_________________________________________________________________________________\n");
		printf("|学号\t\t|姓名\t|性别\t|高数\t|英语\t|大物\t|离散\t|线代\t|总分\t|\n");
		printf("_________________________________________________________________________________\n");
		printf("|%lld\t|%s\t|%s\t|%d\t|%d\t|%d\t|%d\t|%d\t|%d\t|\n",st->id,st->name,st->sex,st->math,st->english,st->phisics,st->scatter,st->generation,st->sum);
		printf("_________________________________________________________________________________\n");
		printf("**********************************************\n");
		printf("**          修改学生姓名--------- 1         **\n");
		printf("**          修改学生性别--------- 2         **\n");
		printf("**          修改高数成绩--------- 3         **\n");
		printf("**          修改英语成绩--------- 4         **\n");
		printf("**          修改大物成绩--------- 5         **\n");
		printf("**          修改离散成绩--------- 6         **\n");
		printf("**          修改线代成绩--------- 7         **\n");
		printf("**********************************************\n");
		printf("请输入要修改的信息:");
		 
		scanf("%d",&choice);
		switch (choice){
			case 1:
				printf("请输入姓名(中文最长不要超过4个字):");
				while(1){
					scanf("%s",st->name);
					node *p=searchname(st->name,L);
					int len=strlen(st->name);
					if(p==NULL&&len<=10){
						break;
					}else{
						printf("该姓名已存在或长度不合理!请重新输入姓名!\n");
					}
				}
				
				break;
			case 2:
				printf("请输入性别(男|女):");
				while(1){
					scanf("%s",st->sex);
					if(strlen(st->sex)==2){
						break;
					}else{
						printf("输入不合理!(性别应为:男 / 女)请重新输入性别!\n");
					}
				} 
				break;
			case 3:
				printf("请输入高数成绩(0~100):");
				while(1){
					scanf("%d",&st->math);
					if(st->math>=0&&st->math<=100){
						break;
					}else{
						printf("输入的分数不合理!应在0~100之间!请重新输入高数成绩!\n");
					}
				}
				break;
			case 4:
				printf("请输入英语成绩(0~100):");
				while(1){
					scanf("%d",&st->english);
					if(st->english>=0&&st->english<=100){
						break;
					}else{
						printf("输入的分数不合理!应在0~100之间!请重新输入英语成绩!\n");
					}
				}
				break;
			case 5:
				printf("请输入大物成绩(0~100):");
				while(1){
					scanf("%d",&st->phisics);
					if(st->phisics>=0&&st->phisics<=100){
						break;
					}else{
						printf("输入的分数不合理!应在0~100之间!请重新输入大物成绩!\n");
					}
				}
				break;
			case 6:
				printf("请输入离散成绩(0~100):");
				while(1){
					scanf("%d",&st->scatter);
					if(st->scatter>=0&&st->scatter<=100){
						break;
					}else{
						printf("输入的分数不合理!应在0~100之间!请重新输入离散成绩!\n");
					}
				}
				break;
			case 7:
				printf("请输入线代成绩(0~100):");
				while(1){
					scanf("%d",&st->generation);
					if(st->generation>=0&&st->generation<=100){
						break;
					}else{
						printf("输入的分数不合理!应在0~100之间!请重新输入线代成绩!\n");
					}
				}
				break;
		}
		st->sum=st->math+st->english+st->phisics+st->scatter+st->generation;
		printf("是否继续修改该学生的信息?(是:1/否:0)\n");
		scanf("%d",&choice);
		if(choice==0){
			printf("修改成功!修改后该学生的信息与成绩如下:\n");
			break;
		}	
	}
	printf("_________________________________________________________________________________\n");
	printf("|学号\t\t|姓名\t|性别\t|高数\t|英语\t|大物\t|离散\t|线代\t|总分\t|\n");
	printf("_________________________________________________________________________________\n");
	printf("|%lld\t|%s\t|%s\t|%d\t|%d\t|%d\t|%d\t|%d\t|%d\t|\n",st->id,st->name,st->sex,st->math,st->english,st->phisics,st->scatter,st->generation,st->sum);
	printf("_________________________________________________________________________________\n");
	saveFile(L);
} 

定义结构体指针变量,调用之前写的searchid()函数,输入要修改的学生的学号,对链表进行遍历,找不到输出“查无此人”,因为定义的searchid()函数返回的是要修改成绩的学生的上一个学生,所以要将指针变量指向下一个节点

因为要修改的可能不止一科,所以修改我用一个和主函数里一样的死循环来进行判断要修改的信息,来实现对同一个学生成绩的多次修改,同时,为了加强下数据,用死循环来判断输入的学生信息是否符合格式。

修改完毕后输出修改后该学生的成绩并将修改后的成绩保存到文件中

这样,就可以实现修改学生成绩的功能了。

(7)成绩删除

和成绩查找与成绩修改一样的,输入学号,定义结构体指针变量,调用searchid函数,对链表进行遍历,按学号找到学生并将该学生的成绩从链表中删除,自定义将某一节点从链表中删除的deletestu函数

void deletestu(node *pr){//删除学生信息 
	node *s=pr->next; 
	pr->next=s->next;
	s->next=NULL;
	free(s);//释放节点空间 
}

void delete_student(node *L){//删除学生信息 
	system("cls");
	long long id; 
	node *p;
	printf("请输入删除学生的学号(10位数):");
	scanf("%lld",&id);
	node *st=searchid(id,L);
	p=st;
	if(st==NULL){
		printf("查无此人!");
		return; 
	}
	st=st->next;
	printf("_________________________________________________________________________________\n");
	printf("|学号\t\t|姓名\t|性别\t|高数\t|英语\t|大物\t|离散\t|线代\t|总分\t|\n");;
	printf("_________________________________________________________________________________\n");
	printf("|%lld\t|%s\t|%s\t|%d\t|%d\t|%d\t|%d\t|%d\t|%d\t|\n",st->id,st->name,st->sex,st->math,st->english,st->phisics,st->scatter,st->generation,st->sum);
	printf("_________________________________________________________________________________\n");
	deletestu(p);
	saveFile(L);
}

这样,删除学生成绩的功能就实现啦

(9)成绩排序和成绩输出

可以知道,在之前输入学生成绩的时候,用的是头插法,也就是说,每次输入的成绩都会被排在第一位,所以要对成绩进行排序才能将成绩输出。

定义函数ScoreSort()函数和布尔型函数cmp(作为定义排序规则)进行排序,不理解建议百度:C语言中的cmp函数

bool cmp(node a,node b){//排序比较规则 
	return a.sum>b.sum;//从大到小排序 
}

void ScoreSort(node *L){//将学生成绩进行排序,从高到低 
	for(node *p=L->next;p!=NULL;p=p->next){
		for(node *q=p;q!=NULL;q=q->next){
			if(!cmp(*p,*q)){//如果不符合排序规则则进行交换 
				//交换数据域
				node t=*p;
				*p=*q;
				*q=t;
				//交换指针域
				t.next=p->next;
				p->next=q->next;
				q->next=t.next; 
			}
		}
	}
}

排序完之后,定义maxscore()函数找出各科的最高分和总分的最高分:

int maxscore(node *L,int mode){//查找各科最高分 
	int max=0; 
	node *p=L->next;
	if(mode==1){//高数最高分 
		while(p!=NULL){
			if(p->math>max){
				max=p->math;
			}
			p=p->next;
		}
		return max;
	}else if(mode==2){//英语最高分 
		while(p!=NULL){
			if(p->english>max){
				max=p->english;
			}
			p=p->next;
		}
		return max;
	}else if(mode==3){//大物最高分 
		while(p!=NULL){
			if(p->phisics>max){
				max=p->phisics;
			}
			p=p->next;
		}
		return max;
	}else if(mode==4){//离散最高分 
		while(p!=NULL){
			if(p->scatter>max){
				max=p->scatter;
			}
			p=p->next;
		}
		return max;
	}else if(mode==5){//线代最高分 
		while(p!=NULL){
			if(p->generation>max){
				max=p->generation;
			}
			p=p->next;
		}
		return max;
	}else if(mode==6){//总分最高分 
		while(p!=NULL){
			if(p->sum>max){
				max=p->sum;
			}
			p=p->next;
		}
		return max;
	}
	
}

通过mode的值遍历链表找出各科成绩的最高分

后面就将排序后的成绩进行和各科最高分进行输出

void output_sort(node *L){//输出学生信息 
	system("cls");
	ScoreSort(&List);
	node *p=L->next;
	if(p!=NULL){
		printf("_________________________________________________________________________________\n");
		printf("|学号\t\t|姓名\t|性别\t|高数\t|英语\t|大物\t|离散\t|线代\t|总分\t|\n");
		printf("_________________________________________________________________________________\n");
		while(p!=NULL){
			printf("|%lld\t|%s\t|%s\t|%d\t|%d\t|%d\t|%d\t|%d\t|%d\t|\n",p->id,p->name,p->sex,p->math,p->english,p->phisics,p->scatter,p->generation,p->sum);
			printf("_________________________________________________________________________________\n");;
			p=p->next;
		}
	}
	
	printf("高数最高分:%d  ",maxscore(&List,1));printf("英语最高分:%d\n",maxscore(&List,2));
	printf("大物最高分:%d  ",maxscore(&List,3));printf("离散最高分:%d\n",maxscore(&List,4));
	printf("线代最高分:%d  ",maxscore(&List,5));printf("总分最高分:%d\n",maxscore(&List,6)); 
	
}

这样,就可以实现成绩的排序和输出了。

(10)密码修改

密码的修改并没有用到什么高级的操作,就是简单的应用条件语句和strlen()函数进行循环,然后用文件将修改后的密码保存。

先看保存密码的函数:

void save_password(char s[]){//将修改后的密码保存 
	FILE *fp= fopen("password.txt","w+");
	fprintf(fp,"%s",s);  
	fclose(fp);
}
int change(){
	printf("注:修改密码后需要重新登录!\n"); 
	printf("请输入原来的密码:\n");
	while(1){
		hide_password(key,21);
		printf("\n");
		int n=strlen(key),m=strlen(password);
		if(n!=m){
			printf("密码错误!\n");
		}else{
			int sum=0;
			for(int i=0;i<n;i++){
				if(key[i]!=password[i]){
					sum++;
				}
			}
			if(sum==0){
				break;
			}else{
				printf("密码错误!\n");
			}
		}
	}
	printf("请输入想要更改的密码:\n");
	hide_password(password,21);
	save_password(password); 
	printf("\n修改成功!请重新登录!\n");
	enter();
	return 0;
}

四、程序运行结果展示

执行代码

输入密码123456(隐藏了输入),密码的输入连续错5次直接退出程序:

 登录成功后显示菜单界面。

选择功能1,输入数字1,增加学生信息,按提示依次输入要增加的学生信息

一个学生的成绩输入后,会询问是否需要继续输入学生信息,这里我就不进行继续输入了。

若输入的学号已经在链表中存在:

或是输入的姓名已存在:

还有就是,如果输入的学生信息不对,系统会进行提示并要求重新输入:

输入1回车继续执行代码 

输入5检验学生成绩是否成功输入

在排名末位找到王项,说明成绩成功输入文件中

输入1回车继续操作,再输入2进行成绩删除,删除李四同学的成绩

输入1继续操作,再输入5输出成绩排名查看李四同学的成绩是否被删除

输出的排名中没有李四同学,删除成绩成功

小于同学成绩输错了,输入1继续执行后输入3对小于同学的成绩进行修改

输入小于同学的学号

依次对小于的英语、线代成绩进行修改

输入0表示修改完毕,会输出修改后小于同学的成绩:

成绩修改完毕,输入1继续执行,再输入5输出成绩排名检验

发现小于同学的排名由第一变为了第二,且成绩符合刚才的修改,说明修改成功。

输入1继续执行,再输入4单独查寻王伟同学的成绩

先按学号查询

按学号查询后输入1继续执行代码

再输入4,再按姓名进行查询

输入1回车,再输入5,输出成绩排名:

最后检验一下功能6,修改密码

输入原来的密码123456,改为输入sjj666(都是隐藏输入)。

回车,退回登录界面

输入sjj666密码登录:

输入1继续操作,最后输入0结束程序:

最后查看一下文件里的信息:

然后看一下保存密码的文件password.txt的内容:

到这里,班级成绩管理系统的功能就展示完毕。

五、设计总结

        刚开始进行程序设计的时候,是完全没有方向的,在b站找了些视频和在CSDN找了几篇博客看了之后才大概明白方向。但是我指针、链表、文件都没有学,然后我又拿出上学期发的课本《C语言程序设计》来看,慢慢理解指针、链表还有文件。一开始我是完全看不懂的,我就去逛b站,看CSDN上的博客。慢慢搞懂了指针、链表和文件的操作,当然,做的项目比较简单(写完了我才觉得简单),我仅仅只是学了点基础的操作,不过我觉得后面继续深入得学习是比较容易的了,毕竟有了点基础。

        在调试的过程中,我还是发现了挺多的bug的,比如说:读取文件失败、每次运行的程序结束后,无法保存链表中的数据;特别郁闷的是,我代码已经写完了,报告也写完了,然后检查的时候又发现:我没有对相同的学号、姓名进行限制。还好问题不大,将searchidsearchname函数调到前面,在输入学号和姓名的时候使用死循环,遍历一下链表就解决了。后面和同学聊天时,听到同学说一次只能输入一个学生的成绩太少了,现实里真正的输入成绩肯定是多个输入的。他的话瞬间启发了我,我再次使用一个死循环实现了成绩的连续多次输入。这还让我联想到:成绩的删除、成绩的查找和成绩的修改等都可以用死循环来进行连续的操作,我正想行动,又感觉太啰嗦了,而且没有必要,因为现实中的成绩删除、查找和修改都是个例,因此我就没有实现这一想法。然后因为报告已经是写完了,我又得再次修改。这确实是一个精益求精的过程,就像是真正的要做一个系统,必须得为用户的使用舒适度着想,按用户需求设计系统功能,这样才能设计出受大众欢迎的产品。

对这个系统其实还有几个想实现的功能没有实现:

1个是修改后密码的储存,我虽然设置了修改密码这个功能,但是这个功能其实是没什么用的,因为修改密码后结束了程序,密码就会重置为初始密码123456

2个是排名的显示,我的程序在输出排名的时候虽然是按照总分的高低排列的,但是是没有显示具体排名的。

3个是输出成绩排名没有按高数成绩高低排序或是按某一科成绩排名的选项。

4个是没有输入的学生学号和姓名等进行具体的限制,在我写的程序里,学号和姓名可以是任意长的。当输入的学号或姓名长了或是短了的时候,会影响输出成绩排名时的排版,而我对这个问题仅仅只是在输入时对输入的信息进行了要求,没有强制性,这样容易造成排版混乱。

       在写课设的过程中,虽然有点累,但是学到的东西还是挺多的,也进一步优化了我的学习方式,对编程的学习兴趣也提高了,后面会继续努力学习的。

2021年7月25号补充:

       24号晚上我进行了答辩,我说到没用程序结束后会重置密码的问题后,老师问我:那为什么不用文件来将密码保存呢?我一下被问住了,我是想到可以这样来解决密码重置的问题,但是我刚刚加入这个功能的时候,我对文件还没那么熟,就想着算了;还有就是学号太大,数据爆掉的问题,这个问题我也是提交了报告之后测试才发现的问题还有就是姓名、性别、成绩这些我都没有进行具体的限制。答辩后才发现我太敷衍了,班里大佬们做的课设都好NB,我还想偷工减料,太差劲了,所以第二天我就将这两个bug改了。

2021年12月16日补充:

        又发现了一个bug,是我的问题,因为这些代码一直是在我本地运行的,所以在我的设想中,初始密码应该是123456,但是,根据一位同学的反映,将代码复制到本地运行之后,初始密码不是123456,初始密码就是没有,直接enter就可以进入界面了。我今天马上测试了一下,果然是这样,但是修改密码这个操作是没有问题的,而且,第一次在本地进行运行,是不会生成password.txt这个保存密码的文件的,只有在修改过一次密码之后才会出现。

        总之,是发现了一个问题,主要原因是没能从使用者的角度考虑问题,但是因为最近比较忙,要准备期末考试这些,所以还没有时间来进行具体的修改(现在我的草稿箱已经有三篇文章没写完了,只能用寒假来解决了哈哈)。大家拿代码去运行发现什么问题也可以私信博主哦~感谢大家!

2022年4月16日补充:

        今天来修改一下之前12月份发现的问题。也就是初始密码没有的问题。

        其实比较简单,先读取文件,判断文件是否为空,为空就将密码设置为123456 。否则就进行密码输入和判断。

2023年12月29日补充:

        有同学指出无法修改学生姓名,看了一下发现是在输入新的姓名时就将姓名改好了,所以查重时一直报重名,现在代码已经改正了。感谢这位同学指出问题。因为是大一的时候写的。现在回来看这代码的结构体,感觉确实不太好。

六、源代码

                虽然我写的课设是比较简单的,但是也是有570+行,字数太多了,所以源代码我另外写到另一篇博客里。点击这里获取源代码

参考文章:
C语言详细学生成绩管理系统_北以晨光的博客-CSDN博客_数据结构学生信息管理系统(一样的班级管理系统,是真正的大佬写的)

C语言隐藏密码实现(隐藏密码的函数我是使用这篇文章里面的)

C语言打印爱心代码(打印爱心的代码我是在这找的)

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐