从零开始的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

image-20201226175102218

学生信息删除

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

页面部分

//删除学生信息
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版本到此也就完结了。之后也可以在此基础上进行其他功能的开发,比如,程序排序,最高分,平均分等等的处理。大体都是围绕链表的遍历,插入和删除操作来执行的。再结合自己对界面的设计即可。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


首先GDB是类unix系统下一个优秀的调试工具, 当然作为debug代码的工具, 肯定没有IDE来的直观了. 不过, 命令行自然有命令行的有点, 当你无法是使用IDE时, gdb有时会帮上很大的忙. 下面用1个例子来说明用法吧: 建立1个目录testgdb2 1. 编写c文件test.c可以见到在2
1. C语言定义1个数组的时候, 必须同时指定它的长度.例如:int a[5]={1,2,3,4,5}; //合法int b[6]; //合法int c[]; //错误 因为没有指定长度但是下面语句是正确, 它隐形定义了数组的长度, 就是赋值元素的个数int d[] ={4,5,6,7,8,9} /
C++的auto关键字在C+⬑新标准出来之前基本上处于弃用的状态,到C+⬑新标准发布之后,auto关键字被赋予了新的意义,并且功能也变得很强大,此篇文章重点介绍auto关键字的新功能、新用法,以及在C+⬔、C+⬗、C+⬠各版本中对它的使用缺陷和限制不断地修正和增强,当然auto关
const关键字是用于定义一个不该被改变的对象,它的作用是告诉编译器和其他程序员不允许修改这个对象的值或者状态。当程序员看到使用const修饰的代码时就知道不应该修改对应对象的值,而编译器则会强制实施这个约束,任何违反这个规定的代码会在编译期间报错。它可以用于任何函数或者类之后的全局或namespa
文章浏览阅读315次。之前用C语言编过链表,这几天突然想用C++编一下链表,搞了大半天才搞出来,所以就赶紧整理一下记录下来,省的万一时间长了找不到代码哈哈。一、链表代码1、Node.h文件代码#pragma onceclass Node{public: int ID; char alph; Node* next; Node(int ID,char alph); ~Node();private:..._if(current->id==id)
文章浏览阅读219次。碰到问题就要记录下来,防止遗忘吧。文章目录一、VS中的命令行参数二、内联函数和宏三、初始化和赋值一、VS中的命令行参数今天在运行代码的时候,碰都了下面的情况: // 解析命令行参数 if (pcl::console::find_argument (argc, argv, "-h") >= 0) { printUsage (argv[0]); return 0; }..._"if (pcl::console::find_argument(argc, argv, "-f") >= 0)怎么输入参数"
文章浏览阅读1.8k次,点赞11次,收藏37次。因为自己对决策树的机制非常的好奇,所以就研究了一下决策树的ID3算法,在这也做一篇笔记记录一下过程。文章目录一、什么是决策树?二、信息增益2.1信息熵2.1.1定义2.1.2演变2.2信息增益三、ID3算法实现四、小结一、什么是决策树?这个问题是我从一开始就有的疑问,什么是决策树?在看了一些资料之后,因为没有看到书上给出具体定义,所以按照我自己的理解决策树就是通过一个个“决策”而构建的一种树状结构,而且决策树的整个处理机制非常类似于我们人类在面临决策问题时的处理机制,这也可能就是其名字的由来。决_c++id3
文章浏览阅读492次。C++ 设计模式之策略模式
文章浏览阅读683次。我也算是个C++的小白,对于C++中的谓语我第一时间就想到了C#中的委托,但两者又不尽相同,所以想写一篇笔记记录一下。文章目录一、什么是谓语?二、使用谓语一、什么是谓语?谓语是一个可调用的表达式,其返回的结果可以作为条件的值,在C++中其实就是向算法传递函数。这和C#中的委托的概念其实是一样的,都是将函数作为参数进行传递。C++标准库中的谓语主要有两类:一元谓语和二元谓语,也就是有的算法只能..._谓语句 c++
文章浏览阅读225次。又看了一遍操作符的东西,感觉之前对操作符的理解还停留在很浅的认知上(仅仅会用哈哈),所以做一下笔记来加深一下印象。文章目录一、为什么会有操作符重载?二、操作符重载作用的对象一、为什么会有操作符重载?如果要回答这个问题,我们其实应该仔细想一下如果没有操作符重载会怎样呢?这其实很容易就联想到了C语言,因为他就没有操作符重载这一说。虽然C语言中没有类class这一概念,但是他有着和类及其相似的结构..._6-6 我的朋友 - c/c++ 操作符重载分数 15作者 海洋饼干叔叔单位 重庆大学实现frie
文章浏览阅读216次。因为之前碰到了很多关于C++上的问题,现在整理并记录一下。文章目录一、引用一、引用在C++中,引用就是给对象起了另一个名字,也就是“对象别名”。感觉和什么东西很相似,仔细一想不就是类型别名“typedef”吗哈哈。它其实是和原对象形成了一种绑定的一种关系,..._vc++6.0报错:returning address of local
文章浏览阅读565次。因为一直好奇预处理器的工作机制,所以就查了查书,做一下自己看完书之后的笔记。文章目录一、预处理器的作用一、预处理器的作用_c语言预处理器作用
文章浏览阅读1.8k次,点赞3次,收藏10次。最近特别查阅了一下关于C++文件的输入/输出的资料,整理了一下就写一下笔记。文章目录一、什么是流二、什么是缓冲区三、代码实现文件IO3.1 使用文件流对象读取数据3.2重定向一、什么是流当前的计算机具有很多种设备,但是无论是哪种设备都要与数据和信息进行打交道,所以这就牵扯到设备与数据之间的I/O操作。而每种设备又有着不同的特性和操作协议,由于过于复杂,所以我们一般是不会和这些通信细节打交道的..._c++ inpath
文章浏览阅读4.8k次,点赞6次,收藏29次。因为要使用到C++的动态链接库,所以就特意网上找了一下资料实现了一下。文章目录一、lib与dll文件二、创建dll文件三、dll隐式链接四、显式链接五、小结一、lib与dll文件之前我一直以为动态链接库就是指dll文件,这也是C#给我造成的一种印象,因为在C#中建立的类库文件都是dll文件,而且只要简单引用就可以了,但是C++却并不是这样的,这可能是因为C#隐藏了一些细节的缘故吧。在C++中共有两种库模式,一种是包含lib和dll两种文件,这种情况下其中的lib文件包含了函数所在的dll文件和dl_c++调用动态链接库
文章浏览阅读973次。因为遇到了一这个操作符的问题,所以记录一下出现的问题*~*。一、问题描述二、产生原因因为也是第一次出现这个问题,所以就到网上查了一些资料和书籍,现在倒也大概理解这个错误出现的原因了。有时候举个例子可能更容易理解为啥会出现这个错误,就拿一本书中的例子来说一下,如下所示:template<class T> class NamedObject { public: NamedObject(std::string& nameVal, const T objectVal) __copy_assign报错
C语言中的单向链表可以解决数组和结构体在使用时的内存连续性问题,同时还能动态地调整长度。本文介绍了单向链表的结构和基本操作,并给出了一个简单的示例代码。
文章浏览阅读2.3k次。区分'0'、"0"、0、''_0和
文章浏览阅读5.8k次,点赞4次,收藏8次。C语言函数指针详解,微剖本质_c语言指针函数
数组指针和指针数组是代码中常见的定义形式。虽然它们的语法类似,但含义完全不同。对于一维数组而言,数组名即为首元素的地址,不需要取址即可赋值给指针。而对于二维数组,数组名代表首行元素的地址,可以看作是一个指针数组,需要使用取址操作。
文章浏览阅读297次。总结刚入门的新同学C语言编程常见的低级错误