从零开始的C程序设计大作业——学生成绩管理系统

前言

生成绩管理系统可以说是C语言程序设计的结课的必备大作业了。花了些时间,费了些头发肝了下,完成了两个系统,一个是控制台版本的,另一个用easyx图形库进行了优化。
先放出完成后的演示图片占个坑。具体的实现过程,等我再梳理下,再慢慢更新整理到论坛上来。

演示DEMO

基础控制台版本

image.png


输出所有学生信息

控制台版本开发过程整理

开发环境

系统: win10

IDE: Dev Cpp

前置知识

需要掌握基础的C语言知识

  • 顺序结构
  • 分支结构
  • 循环结构
  • 数组、字符串
  • 函数
  • 结构体、指针
  • 链表
  • 文件操作

功能分析

工欲善其事必先利其器,先分析好整体功能和大体的布局再慢慢动手进行代码的实现。
基础设想是,先显示菜单,通过输入数字选择对应的功能包括增加学生信息,删除学生信息,修改学生信息,查询学生信息以及退出程序功能

image.png

菜单界面实现

使用输出语句来实现界面。计划使用数字键来代表各自的功能

//主菜单界面 
void welcome(){
	printf("************************\n");
	printf("**  学生成绩管理系统  **\n");
	printf("**      作者:咸鱼君  **\n");
	printf("**                    **\n");
	printf("**  增加学生信息 ---1 **\n");
	printf("**  删除学生信息 ---2 **\n");
	printf("**  修改学生信息 ---3 **\n");
	printf("**  查询学生信息 ---4 **\n");
	printf("**  输出学生信息 ---5 **\n");
	printf("**  退出管理系统 ---0 **\n");
	
	printf("请输入对应的功能键(数字): ");
}

image.png

功能框架搭建

先处理好整个功能框架,通过输入数字,进行分支判断,不同的数字代表不同的功能。先将要实现的功能做个简易的版本出来,之后再慢慢填充细节。
想的是执行完某些功能后还能继续进行操作,所以将程序放入循环中,并在操作执行完成后,询问继续操作,再根据选择进行处理。

int main(){
	int choice=0;
	while(true){
		welcome();
			scanf("%d",&choice);
			switch(choice){
				case 1://增加学生信息 
					addStuInfo(); 
					break;
				case 2://删除学生信息
					deleteStuInfo();
					break;
				case 3://修改学生信息 
					fixStuInfo();
					break;
				case 4://查询学生信息
					searchStuInfo();
					break;
				case 5://输出学生信息
					printStuInfo();
					break;
				case 0://退出程序 
					goodBye();
					break;						
			}
		printf("是否需要继续操作?(yes:1 / no:0 ):");
		scanf("%d",&choice);
		if(choice==0){
			break;
		}
	}
	
	return 0;
}

tLRiEed7FC.gif

数据结构定义

围绕学生信息进行处理,那么思考学生具有哪些信息。包含,学号,姓名,性别,语文,数学,英语成绩,还有个总分。将对应属性集合在一块,采用结构体方式进行数据操作。并使用链表的方式将数据进行串联。

typedef struct Node{
	int id;//学号
	char name[30];//姓名 
	char sex[10];//性别
	int ch;//语文
	int ma;//数学
	int en;//英语
	int sum;//总分
	
	struct Node *next;//指向下一个结点 
}node;

文件数据的读取

这此程序的数据都要以文件的形式进行信息的保存,如果想要在屏幕上输出数据,那么得先读取文件中的信息才行。
C语言中文件读取操作要使用文件指针和相关函数,格式如下。

FILE *fpr=fopen("文件名","操作方式");
fscanf(fpr,"%d",&intValue);

文件名需要加上后缀名,操作方式因为是要从文件中读取信息,所以写r。如果是进行信息的写入则是w。
之后需要将读取的信息以链表的方式组织起来,打算采用尾插法的方式插入数据。

// 尾插法 
t->next=s;//链表尾指针 的后一个结点指向新结点 
t=s;//更新尾结点 
t->next=NULL;//链表尾指针 的后一个结点指向NULL 

读取函数

// 文件输入
int readFile(Node *L){
	FILE *fpr=fopen("studentInfo.txt","r");
	node *t=L;
	node st;
	node *s;
	if(fpr==NULL){
		return 0;
	}else{
		
		//fscanf()
		while(fscanf(fpr,"%d %s %s %d %d %d %d",&st.id,st.name,st.sex,&st.ch,&st.ma,&st.en,&st.sum)!=EOF){
			
			s=(node *)malloc(sizeof(node));
			*s=st;
			
			// 尾插法 
			t->next=s;//链表尾指针 的后一个结点指向新结点 
			t=s;//更新尾结点 
			t->next=NULL;//链表尾指针 的后一个结点指向NULL 
			
		}
	}
	fclose(fpr);//关闭文件指针
	return 1;
}

输出所有学生信息

接下来完成所有学生信息的输出。此处需要考察链表的遍历。

void printStuInfo(node *L){
	 system("cls");
	 node *p=L->next;
	 printf("________________________________________________________\n");
	 printf("|学号\t|姓名\t|性别\t|语文\t|数学\t|英语\t|总分\t|\n");
	 printf("________________________________________________________\n");
	 if(p!=NULL){
	 	
	 	while(p!=NULL){
			printf("%d|%s\t|%s\t|%d\t|%d\t|%d\t|%d\t|\n",p->id,p->name,p->sex,p->ch,p->ma,p->en,p->sum);
			printf("________________________________________________________\n");
			p=p->next;
		}
	 }
}

image-20201225155709522

增加学生信息

接下来是增加学生的信息,此处采用头插法将链表结点进行插入。将学生信息的增加分成了两部分,一部分是界面的打印,一部分是底层数据的处理。

image-20201225180440775

界面实现:

//增加学生信息
void printAddStuInfo(){
	// 
	system("cls");
	node st;
	printf("请输入新增学生相关信息\n");
	printf("学号:");
	scanf("%d",&st.id);
	printf("姓名:");
	scanf("%s",st.name);
	printf("性别:");
	scanf("%s",st.sex);
	printf("语文:");
	scanf("%d",&st.ch);
	printf("数学:");
	scanf("%d",&st.ma);
	printf("英语:");
	scanf("%d",&st.en);
	st.sum=st.ch+st.ma+st.en;
	
	insertStuInfo(&List,st);
	 
}

功能实现:

void insertStuInfo(node *L,node e){
	//头插法
	node *h=L;
	node *s=(node *)malloc(sizeof(node));
	*s=e;
	
	s->next=h->next;
	h->next=s;
}

image-20201225161643690

image-20201225161603787

文件数据的写入

这部分和文件的读取部分相似,思路是将整个链表内容存储到文件中。

使用fprintf()将文件信息进行存储。

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

再在学生信息的增加过程中添加文件数据的保存操作。

void insertStuInfo(node *L,node e){
	//头插法
	node *h=L;
	node *s=(node *)malloc(sizeof(node));
	*s=e;
	
	s->next=h->next;
	h->next=s;
	
	//保存文件 
	saveFile(L);
}

6nW6K36jsj

Ne41ND5ZLg

学生信息查询

接下来是实现学生信息查询功能,计划也是页面输出部分与逻辑实现部分进行分离。打算,可以通过学号与姓名两个关键值进行信息的查找。因为是链表结构,为了方便之后的操作,逻辑函数会返回查找到的学生信息的前一个结点位置,这样的话也能在删除学生信息与修改学生信息中进行函数的复用了。

界面实现:

//查询学生信息
void printSearchStuInfo(node *L){
	system("cls");
	int choice=0;
	int id;
	char name[50];
	node *st;
	printf("按学号查询----- 1\n");
	printf("按姓名查询----- 2\n");
	printf("请输入查询方式:");
	scanf("%d",&choice);
	
	if(choice == 1){
		printf("请输入要查询的学号:");
		scanf("%d",&id);
		st=searchStuInfoById(id,L);
		
		if(st==NULL){
			printf("查无此人!\n");
		}else{
			st=st->next;
			printf("________________________________________________________\n");
			printf("|学号\t|姓名\t|性别\t|语文\t|数学\t|英语\t|总分\t|\n");
			printf("________________________________________________________\n");
			printf("%d|%s\t|%s\t|%d\t|%d\t|%d\t|%d\t|\n",st->id,st->name,st->sex,st->ch,st->ma,st->en,st->sum);
			printf("________________________________________________________\n");
		}
	}else if(choice ==2){
		printf("请输入要查询的姓名:");
			scanf("%s",name);
			st=searchStuInfoByName(name,L);
			
			if(st==NULL){
				printf("查无此人!\n");
			}else{
				st=st->next;
				printf("________________________________________________________\n");
				printf("|学号\t|姓名\t|性别\t|语文\t|数学\t|英语\t|总分\t|\n");
				printf("________________________________________________________\n");
				printf("%d|%s\t|%s\t|%d\t|%d\t|%d\t|%d\t|\n",st->sum);
				printf("________________________________________________________\n");
			}
	}
	
}

逻辑实现:

思路是遍历整个链表,逐一对关键信息进行比较。

按学号进行查找,找不到返回NULL,找到了返回前一个结点位置

//按学号进行查找 
node * searchStuInfoById(int id,node *L){
	
	node *p=L;
	
	while(p->next!=NULL){
		
		if(p->next->id==id){
			return p;
		}
		
		p=p->next;
	}
	
	return NULL;
}

按姓名进行查找,找不到返回NULL,找到了返回前一个结点位置

//按姓名进行查找 
node * searchStuInfoByName(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;
}

4aoMYPA7rj

Mk9h8hz0gF

学生信息修改

依旧是分成两部分,先输出界面,过程逻辑的话就沿用学生信息查询的部分。实现逻辑是这样的:先查到要查询的学生信息,在对信息修改,改完了再保存到文件中。

页面和实现部分:

//修改学生信息
void printFixStuInfo(node *L){
	system("cls");
	int id;
	int choice=-1;
	
	printf("请输入要查找的学生学号");
	scanf("%d",&id);
	node *st=searchStuInfoById(id,L);
	
	if(st==NULL){
		printf("查无此人!");
		return;
	}
    
	st=st->next; 
	
	while(1){
		system("cls"); 
		printf("________________________________________________________\n");
		printf("|学号\t|姓名\t|性别\t|语文\t|数学\t|英语\t|总分\t|\n");
		printf("________________________________________________________\n");
		printf("%d|%s\t|%s\t|%d\t|%d\t|%d\t|%d\t|\n",st->sum);
		printf("________________________________________________________\n");
		printf("修改姓名---- 1\n");
		printf("修改性别---- 2\n");
		printf("修改语文---- 3\n");
		printf("修改数学---- 4\n");
		printf("修改英语---- 5\n");
		
		printf("请输入要修改的信息: ");
		scanf("%d",&choice);
		
		switch(choice){
			case 1:
				printf("请输入姓名:");
				scanf("%s",st->name);
				break;
			case 2:
				printf("请输入性别:");
				scanf("%s",st->sex);
				break;
			case 3:
				printf("请输入语文:");
				scanf("%d",&st->ch);
				break;
			case 4:
				printf("请输入数学:");
				scanf("%d",&st->ma);				
				break;
			case 5:
				printf("请输入英语:");
				scanf("%d",&st->en);				
				break;
		}
		st->sum=st->ch+st->ma+st->en; 
		printf("是否继续修改学生信息?(y-1 / n-0)\n");
		scanf("%d",&choice);
		if(choice == 0){
			break;
		}
	}
	
	printf("________________________________________________________\n");
	printf("|学号\t|姓名\t|性别\t|语文\t|数学\t|英语\t|总分\t|\n");
	printf("________________________________________________________\n");
	printf("%d|%s\t|%s\t|%d\t|%d\t|%d\t|%d\t|\n",st->sum);
	printf("________________________________________________________\n");
	
	//保存文件信息
	saveFile(L);
}

image-20201226175035839

@H_502_233@

学生信息删除

接下来实现学生信息删除部分。页面部分输出提示,之后输入学号查询删除的学生信息。利用之前实现的查询信息的函数得到结点位置,之后再根据位置删除对应的结点,再将修改后的信息保存至文件中去。

页面部分

//删除学生信息
void printDeleteStuInfo(node *L){
	system("cls");
	int id;
	
	node *p;
	
	printf("请输入要查找的学生学号");
	scanf("%d",L);
	p=st;
	
	if(st==NULL){
		printf("查无此人!");
		return;
	}
	
	st=st->next; 
	printf("________________________________________________________\n");
	printf("|学号\t|姓名\t|性别\t|语文\t|数学\t|英语\t|总分\t|\n");
	printf("________________________________________________________\n");
	printf("%d|%s\t|%s\t|%d\t|%d\t|%d\t|%d\t|\n",st->sum);
	printf("________________________________________________________\n");
	
	deleteStuInfo(p);
	saveFile(L);	
 	
}

结点删除部分

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

image-20201226210348062

70euw6DARL

image-20201226210102154

结束界面

到此为止,这个小系统的基础功能都已经完成了。接下来把结束界面处理下。

//退出程序
void goodBye(){
	system("cls");
	printf("欢迎下次使用~\n");
	exit(0);//结束程序 
}

ConsolePauser_F0B8oTbtcU

生成绩管理系统V1.0版本到此也就完结了。之后也可以在此基础上进行其他功能的开发,比如,程序排序,最高分,平均分等等的处理。大体都是围绕链表的遍历,插入和删除操作来执行的。再结合自己对界面的设计即可。

相关文章

/** C+⬑ * 默认成员函数 原来C++类中,有6个默认成员函数: 构造函数 析构函数 拷贝...
#pragma once // 1. 设计一个不能被拷贝的类/* 解析:拷贝只会放生在两个场景中:拷贝构造函数以及赋值运...
C类型转换 C语言:显式和隐式类型转换 隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译...
//异常的概念/*抛出异常后必须要捕获,否则终止程序(到最外层后会交给main管理,main的行为就是终止) try...
#pragma once /*Smart pointer 智能指针;灵巧指针 智能指针三大件//1.RAII//2.像指针一样使用//3.拷贝问...
目录<future>future模板类成员函数:promise类promise的使用例程:packaged_task模板类例程...