目录
一、前言(学习请从头开始看)
四、完整代码(搬运请看这)
一、前言
相信很多大一的同学在做c语言程序设计期末大作业的时候,都会被这些题目所困扰:学生成绩管理系统、学生信息管理系统、图书借阅管理系统、通讯录、选课系统......上网百度到别人的代码,有的人用C++写,有的人用类C写,有的人用C写,有的人用链表写,有的人用顺序表(结构体数组)写,搬运完别人的代码,还得自己看懂并且修改,以防和别的同学雷同。但不同的学校采用不同的c语言教材,不同的老师又有不同的理念:有的老师要求学生使用vb(Visual Basic)来进行编码,有的老师要求学生使用vs(Visual Studio)来进行编码......
vb的缺点是比较死板,规范陈旧,如变量申明必须在函数一开始的时候,否则会报错;vs体量大不说,要求用scanf_s来代替scanf,用fopen_s来代替fopen,并且scanf_s在接收字符串的时候用法和传统的scanf不一样,fopen_s的用法也和fopen稍有不同。个人认为如果不做大型项目,大学4年使用Dev-Cpp足以应付基础的C语言学习和C++学习,Dev-Cpp精巧强大,兼容性较好,不像vb和vs一样纠结于细枝末节,令初学者对C语言的学习望而生畏。一般的Dev-Cpp安装好后默认关闭了调试模式,需要手动打开,这里附上教程。
说完IDE,接下来说说语言:受严蔚敏《数据结构(C语言版)》的影响,很多人在学习数据结构的时候逐渐将C语言代码转变为了类C代码,因为默认使用.cpp文件,类C代码(其实就是C语言代码中混入了少量使代码能够更加简洁的C++的语法)在部分IDE中也能运行。本文所使用的也是类C,部分函数为方便采用了C++的引用符号&(C语言中&是取地址符号,C++中既可以用做取地址也可以用作引用),目的是和传统的数据结构教程相对应,有利于读者后续对数据结构的学习。
通过之前顺序表的学习,相信很多同学都对管理系统的代码结构有了一定的了解,本文用链式结构实现学生成绩管理系统,用到了一些数据结构的思想和理念。
这里简单介绍一些顺序表和链表的区别:顺序表中每个元素在内存中的分布是连续的,当删除中间的某个元素的时候,需要把其后面的所有元素向前移动,比如删除数组a中的第6个元素,那么原先的第7个元素需要移动到现在第6个的位置,原先的第8个元素需要移动到现在第7个的位置......最后再修改顺序表的长度即可;而链表中每个元素(链表中习惯称作节点)在内存中的分布是离散的,当删除中间的某个节点的时候,只需要把该节点的前一个节点和后一个节点进行链接,再释放该节点的空间。
首先定义一个数据结构,再申明操作该数据结构的基本函数,最后实现这些基本函数。
二、要求和思路
2.1 要求
对学生信息(学号、姓名、成绩)进行统一管理,实现增、删、改、查、显,且需要用到文件读写的知识对学生信息进行读取和保存。
2.2 数据结构的定义
数据结构的思想就是基于现有的数据结构(本文的数据结构是链表)和相关函数来实现需求。
链表中的每一个节点都有数据域和指针域,数据域用来存放该节点所存储的信息,指针域用来获取下一个节点在内存中的位置。如下是非常经典的链表节点的定义方式:
typedef struct LNode{//定义节点
ElemType data; //数据域
LNode *next; //指针域
}LNode, *LinkList;
typedef 可以理解为是一个取名字的符号,他将struct LNode这个结构体取了一个简单的代号叫LNode。通常我们定义一个结构体变量,需要这样写struct LNode p,当我们取完代号之后,可以直接写作LNode p。这里举一个一般的例子说明一下
#include<stdio.h>
struct a{
int b;
};
typedef struct a a;
int main(){
a b;
b.b = 2;
printf("%d", b.b);
}
这段代码首先声明结构体a,接着将结构体a取代号为a,即省略了struct。然后在主函数中直接使用a来声明一个结构体变量b,再对他进行赋值和输出。
这段代码和链表节点的定义方式的区别在于,节点的定义方式取代号和申明结构体同时进行,然后用代号在节点结构体中申明一个指针变量,可以说是一举三得。而这段代码就比较中规中矩,便于理解了。肯定有人要问*LinkList是怎么回事,*LinkList和他左边的LNode一样,也是代号,只不过这个代号的类型是指向LNode的指针。可以这样理解,*表示这个代号的类型是LNode类型的指针,LinkList表示这个代号的具体名字。为什么这里要申明一个LNode类型的指针呢?当然是为了编程方便。后面出现的LinkList其实都可以用LNode *来替换。
2.3 基本函数的申明
链表的基本函数有很多,但最最最基础的只有几个,初始化链表、销毁链表、增加节点、删除节点、查找节点、修改节点、打印节点。这里先罗列链表的基本函数:(本文省略了销毁链表)
void CreateList(LinkList &L); //创建链表
void NodeAppend(LinkList &L, ElemType e); //增加节点
int NodeDelete(LinkList &L, int num); //删除节点
int NodeModify(LinkList &L, int num, ElemType e);//修改节点
int NodeSearch(LinkList L, int num, ElemType &e);//查找节点
这里CreateList和NodeAppend的函数类型是void,意思是没有返回值,而其余三个函数的返回值是int,通过返回值来判断删除、修改、查找操作是否成功。当然,创建链表和增加节点的操作也可能不成功,即没有内存空间可以申请,遇到这种情况通常是直接用exit(1)来退出整个程序,但一般来说,这两个操作都是能正常运行的,所以我也就没写异常退出的情况了。
2.3.1 链表初始化
//创建链表(含头节点)
void CreateList(LinkList &L){
L = (LinkList)malloc(sizeof(LNode));
L -> next = NULL;
}
朴实无华的创建一个链表。
2.3.2 尾部增加节点
//尾部增加节点
void NodeAppend(LinkList &L, ElemType e){
LNode *q = (LinkList)malloc(sizeof(LNode));
q -> data = e;
q -> next = NULL;
LNode *p = L;
while(p -> next) p = p -> next;
p -> next = q;
}
一般插入都采用的是头插法,即在链表头部插入,这里为了逻辑更清晰一些,采用了更费时的尾部插入法。首先函数传入的参数是L和e,L是链表,e是新增节点的数据域。要插入一个节点,首先需要申明一个节点空间用于存储e。这里LNode *q = (LinkList)malloc(sizeof(LNode))的作用是申明一个LNode类型的指针q,申明一段LNode大小的空间,并让q指向这段空间。当然也可以写作LNode *q = (LNode *)malloc(sizeof(LNode)),因为LNode * 和 LinkList是等价的。正常来说,q是一个指针,想要对q指向的空间进行赋值,应该使用解地址符*,考虑到q是指针类型,c\c++对结构体的指针类型的数据提供了一个更便捷的符号 ->,用以直接访问该指针指向的结构体的内部成员。当然你也可以使用*来实现,如q -> data = e可以改写为 (*q).data = e。
接着申明一个指针变量p指向链表的头部,通过while循环将p逐步更新,直到p -> next为空,此时p指向链表尾部的节点,用q来更新p -> next实现节点的尾部插入。
2.3.3 删除节点
//根据编号删除节点
int NodeDelete(LinkList &L, int num){
LNode *p,*q;
p = L;
while(p -> next){
q = p -> next;
if(num == (q -> data).num){
p -> next = q -> next;
free(q);
return 0;
}
p = p -> next;
}
return 1;
}
返回0表示删除成功,返回1表示未找到节点,删除失败。
2.3.4 修改节点
//根据编号修改节点
int NodeModify(LinkList &L, int num, ElemType e){
LNode *p,*q;
p = L;
while(p -> next){
q = p -> next;
if(num == (q -> data).num){
q -> data = e;
return 0;
}
p = p -> next;
}
return 1;
}
返回0表示修改成功,返回1表示未找到节点,修改失败。
2.3.5 搜索节点
//根据编号搜索节点,若找到则将节点信息存储至e并返回
int NodeSearch(LinkList L, int num, ElemType &e){
LNode *p, *q;
p = L;
while(p -> next){
q = p -> next;
if(num == (q -> data).num){
e = q -> data;
return 0;
}
p = p -> next;
}
return 1;
}
返回0表示查找成功,返回1表示未找到节点,查找失败。
三、具体构建
实现了基本的数据结构(数据结构和基本操作),现在只要在其基础上增加交互性就能实现成绩管理系统。
首先要定义学生信息结构体,并将其取代号为ElemType来和链表的数据域相统一。
//student结构体存储学生信息
typedef struct student{
int num;//学号
char name[10];//姓名
int score;//分数
}student;
typedef student ElemType;
这里先将struct student取代号为student,再将student取代号为ElemType。当然也可以一步到位。
//student结构体存储学生信息
typedef struct student{
int num;//学号
char name[10];//姓名
int score;//分数
}ElemType;
这里再提供一种方法,请读者自行理解。
//student结构体存储学生信息
typedef struct{
int num;//学号
char name[10];//姓名
int score;//分数
}student;
typedef student ElemType;
3.1 函数申明
void ReadFile(LinkList &L); //读取文件
void WriteFile(LinkList L); //写入文件
void AddStudent(LinkList &L); //增加学生
void DeleteStudent(LinkList &L); //删除学生
void ModifyStudent(LinkList &L); //修改学生
void SearchStudent(LinkList L); //查找学生
void DisplayStudent(LinkList L); //显示学生
void Menu(); //主菜单
3.2 读取文件
//读取文件中的数据到链表
void ReadFile(LinkList &L){
FILE *fp = NULL;
fp = fopen("student.txt", "r");
if(fp == NULL){
printf("Can not open the file\n");
exit(1);
}
CreateList(L);
ElemType temp;
while(fscanf(fp, "%d %s %d", &temp.num, temp.name, &temp.score) != EOF){
NodeAppend(L, temp);
}
fclose(fp);
}
3.3 写入文件
//将链表中的数据用覆盖的方式写入文件中
void WriteFile(LinkList L){
FILE *fp = NULL;
fp = fopen("student.txt", "w");
if(fp == NULL){
printf("Can not open the file\n");
exit(1);
}
LNode *p = L;
ElemType temp;
while(p -> next){
temp = p -> next -> data;
fprintf(fp, "%d %s %d\n", temp.num, temp.name, temp.score);
p = p -> next;
}
fclose(fp);
}
3.4 增加学生
//录入学生信息
void AddStudent(LinkList &L){
ElemType e;
printf("请输入要录入的学生信息:\n");
printf("学号\t姓名\t分数\n");
scanf("%d %s %d", &e.num, e.name, &e.score);
NodeAppend(L, e);
system("pause");
}
3.5 删除学生
//删除学生信息
void DeleteStudent(LinkList &L){
int num;
printf("请输入要删除的学生学号:");
scanf("%d", &num);
if(NodeDelete(L,num) == 1)
printf("未查找到该学生!\n");
else
printf("删除成功!\n");
system("pause");
}
3.6 修改学生
//修改学生信息
void ModifyStudent(LinkList &L){
ElemType e;
int num;
printf("请输入要修改的学生的当前学号:");
scanf("%d", &num);
printf("请输入修改后的信息:学号 姓名 分数\n");
scanf("%d %s %d", &e.num, e.name, &e.score);
if(NodeModify(L, num, e) == 1)
printf("未查找到该学生!\n");
else
printf("修改成功!\n");
system("pause");
}
3.7 查找学生
//查找学生信息
void SearchStudent(LinkList L){
ElemType e;
int num;
printf("请输入要查找的学生学号:\n");
scanf("%d", &num);
if(NodeSearch(L, num, e) == 1)
printf("未查找到改该学生!\n");
else
printf("学号\t姓名\t分数\n%d %s %d\n", e.num, e.name, e.score);
system("pause");
}
3.8 显示学生
//显示学生信息
void DisplayStudent(LinkList L){
printf("学号\t姓名\t分数\n");
LNode *p = L;
ElemType e;
while(p -> next){
e = (p -> next) -> data;
printf("%d %s %d\n", e.num, e.name, e.score);
p = p -> next;
}
system("pause");
}
3.9 主菜单
//主菜单
void Menu(){
system("cls");
printf("------------------------------------------\n");
printf(" 学生管理系统 \n");
printf(" \n");
printf(" 作者微信:qczsbwjzjn \n");
printf(" \n");
printf(" 欢迎交流咨询~ \n");
printf(" \n");
printf(" 1.增加学生信息 2.删除学生信息 \n");
printf(" \n");
printf(" 3.修改学生信息 4.查找学生信息 \n");
printf(" \n");
printf(" 5.显示所有信息 0.退出管理系统 \n");
printf(" \n");
printf("------------------------------------------\n");
printf(" \n");
printf(" 请输入:");
}
3.10 主函数
//主函数
int main(){
LinkList L;
CreateList(L);
ReadFile(L);
int choice = 1;
while(choice){
Menu();
scanf("%d", &choice);
switch(choice){
case 1: AddStudent(L); break;
case 2: DeleteStudent(L); break;
case 3: ModifyStudent(L); break;
case 4: SearchStudent(L); break;
case 5: DisplayStudent(L); break;
case 0: WriteFile(L); break;
}
}
return 0;
}
四、完整代码
#include<stdio.h>
#include <stdlib.h>
//student结构体存储学生信息
typedef struct student{
int num;//学号
char name[10];//姓名
int score;//分数
}ElemType;
typedef struct LNode{//定义节点
ElemType data; //数据域
LNode *next; //指针域
}LNode, *LinkList;
void CreateList(LinkList &L); //创建链表
void NodeAppend(LinkList &L, ElemType e); //增加节点
int NodeDelete(LinkList &L, int num); //删除节点
int NodeModify(LinkList &L, int num, ElemType e);//修改节点
int NodeSearch(LinkList L, int num, ElemType &e);//查找节点
void ReadFile(LinkList &L); //读取文件
void WriteFile(LinkList L); //写入文件
void AddStudent(LinkList &L); //增加学生
void DeleteStudent(LinkList &L); //删除学生
void ModifyStudent(LinkList &L); //修改学生
void SearchStudent(LinkList L); //查找学生
void DisplayStudent(LinkList L); //显示学生
void Menu(); //主菜单
//创建链表(含头节点)
void CreateList(LinkList &L){
L = (LinkList)malloc(sizeof(LNode));
L -> next = NULL;
}
//尾部增加节点
void NodeAppend(LinkList &L, ElemType e){
LNode *q = (LinkList)malloc(sizeof(LNode));
q -> data = e;
q -> next = NULL;
LNode *p = L;
while(p -> next) p = p -> next;
p -> next = q;
}
//根据编号删除节点
int NodeDelete(LinkList &L, int num){
LNode *p,*q;
p = L;
while(p -> next){
q = p -> next;
if(num == (q -> data).num){
p -> next = q -> next;
free(q);
return 0;
}
p = p -> next;
}
return 1;
}
//根据编号修改节点
int NodeModify(LinkList &L, int num, ElemType e){
LNode *p,*q;
p = L;
while(p -> next){
q = p -> next;
if(num == (q -> data).num){
q -> data = e;
return 0;
}
p = p -> next;
}
return 1;
}
//根据编号搜索节点,若找到则将节点信息存储至e并返回
int NodeSearch(LinkList L, int num, ElemType &e){
LNode *p, *q;
p = L;
while(p -> next){
q = p -> next;
if(num == (q -> data).num){
e = q -> data;
return 0;
}
p = p -> next;
}
return 1;
}
//读取文件中的数据到链表
void ReadFile(LinkList &L){
FILE *fp = NULL;
fp = fopen("student.txt", "r");
if(fp == NULL){
printf("Can not open the file\n");
exit(1);
}
CreateList(L);
ElemType temp;
while(fscanf(fp, "%d %s %d", &temp.num, temp.name, &temp.score) != EOF){
NodeAppend(L, temp);
}
fclose(fp);
}
//将链表中的数据用覆盖的方式写入文件中
void WriteFile(LinkList L){
FILE *fp = NULL;
fp = fopen("student.txt", "w");
if(fp == NULL){
printf("Can not open the file\n");
exit(1);
}
LNode *p = L;
ElemType temp;
while(p -> next){
temp = p -> next -> data;
fprintf(fp, "%d %s %d\n", temp.num, temp.name, temp.score);
p = p -> next;
}
fclose(fp);
}
//录入学生信息
void AddStudent(LinkList &L){
ElemType e;
printf("请输入要录入的学生信息:\n");
printf("学号\t姓名\t分数\n");
scanf("%d %s %d", &e.num, e.name, &e.score);
NodeAppend(L, e);
system("pause");
}
//删除学生信息
void DeleteStudent(LinkList &L){
int num;
printf("请输入要删除的学生学号:");
scanf("%d", &num);
if(NodeDelete(L,num) == 1)
printf("未查找到该学生!\n");
else
printf("删除成功!\n");
system("pause");
}
//修改学生信息
void ModifyStudent(LinkList &L){
ElemType e;
int num;
printf("请输入要修改的学生的当前学号:");
scanf("%d", &num);
printf("请输入修改后的信息:学号 姓名 分数\n");
scanf("%d %s %d", &e.num, e.name, &e.score);
if(NodeModify(L, num, e) == 1)
printf("未查找到该学生!\n");
else
printf("修改成功!\n");
system("pause");
}
//查找学生信息
void SearchStudent(LinkList L){
ElemType e;
int num;
printf("请输入要查找的学生学号:\n");
scanf("%d", &num);
if(NodeSearch(L, num, e) == 1)
printf("未查找到改该学生!\n");
else
printf("学号\t姓名\t分数\n%d %s %d\n", e.num, e.name, e.score);
system("pause");
}
//显示学生信息
void DisplayStudent(LinkList L){
printf("学号\t姓名\t分数\n");
LNode *p = L;
ElemType e;
while(p -> next){
e = (p -> next) -> data;
printf("%d %s %d\n", e.num, e.name, e.score);
p = p -> next;
}
system("pause");
}
//主菜单
void Menu(){
system("cls");
printf("------------------------------------------\n");
printf(" 学生管理系统 \n");
printf(" \n");
printf(" \n");
printf(" 1.增加学生信息 2.删除学生信息 \n");
printf(" \n");
printf(" 3.修改学生信息 4.查找学生信息 \n");
printf(" \n");
printf(" 5.显示所有信息 0.退出管理系统 \n");
printf(" \n");
printf("------------------------------------------\n");
printf(" \n");
printf(" 请输入:");
}
//主函数
int main(){
LinkList L;
CreateList(L);
ReadFile(L);
int choice = 1;
while(choice){
Menu();
scanf("%d", &choice);
switch(choice){
case 1: AddStudent(L); break;
case 2: DeleteStudent(L); break;
case 3: ModifyStudent(L); break;
case 4: SearchStudent(L); break;
case 5: DisplayStudent(L); break;
case 0: WriteFile(L); break;
}
}
return 0;
}
五、界面展示
5.1 显示信息
5.2 删除学生
5.3 修改学生
下面附上下载链接(含测试文件)
纯c语言版下载链接如下
纯C语言实现学生成绩管理系统,采用链表结构-C文档类资源-优快云文库https://download.youkuaiyun.com/download/qq_42276781/85833682
六、代码交流
代码交流群:877518298
加群需要审核,如果不想加群可以直接关注微信公众号,回复“管理系统”即可获得代码资源