提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- 0. 准备
- 0-0 头文件
- 0-1 封装学生和结点数据
- 0-2 创建头结点
- 0-3 系统功能提示
- 0-4 根据用户输入选择功能
- 1. 录入学生信息
- 1-0 实现循环录入
- 1-1 程序暂停和清空控制台
- 2. 打印学生信息
- 3. 统计学生人数
- 4. 查找学生信息
- 4-0. 学生信息持久化
- 5. 修改学生信息
- 6. 删除学生信息
- 7. 按成绩排序
- 8. 退出系统
- 总结
前言
本程序使用纯C制作一个学籍管理系统。利用单链表实现学生管理系统,具体功能包括录入学生信息、打印学生信息、统计学生人数、查找学生信息、修改学生信息、删除学生信息、按成绩排序
同时将学生信息保存至文件中,当再次启动程序时,自动读取学生数据。
0. 准备
0-0 头文件
创建StudentManager.h ,加入以下代码
// 标准输入输出
#include <stdio.h>
// _getch:不需要回车,直接获取输入的字符,需要包含conio.h头文件#include <conio.h>
// 动态申请内存
#include <stdlib.h>
0-1 封装学生和结点数据
定义学生结构体,用于保存学生的学号、姓名、成绩信息,并使用typedef给 struct _Student 类型起别名为Student ,方便使用。
typedef struct _Student
{int stuNum; //学号char name[20];//姓名int score; //成绩
}Student;
定义结点结构体,用于保存链表中的结点数据,结点需要保存学生信息及下一个结点的地址,并使用
typedef给 struct _Node 类型起别名为Node ,方便使用。
typedef struct _Node
{Student stu; //学生struct _Node* next; //指向下一个结点的指针
}Node;
0-2 创建头结点
创建StudentManager.c ,加入main函数,并创建链表的头结点,定义head头指针指向头结点
//导入头文件
#include "StudentManager.h"
int main() {//创建头结点Node* head = malloc(sizeof(Node)); head->next = NULL;return 0;
}
0-3 系统功能提示
定义welcome函数,用于提示用户系统功能
void welcome() {printf("*********************************\n"); printf("*\t学生成绩管理系统\t*\n"); printf("*\t请选择功能列表\t\t*\n"); printf("*\t1.录入学生信息\t\t*\n");printf("*\t2.打印学生信息\t\t*\n");printf("*\t3.统计学生人数\t\t*\n"); printf("*\t4.查找学生信息\t\t*\n"); printf("*\t5.修改学生信息\t\t*\n");printf("*\t6.删除学生信息\t\t*\n");printf("*\t7.按成绩排序\t\t*\n"); printf("*\t8.退出系统\t\t*\n");printf("*********************************\n");
}
在头文件中声明函数,并在main函数中调用,后面定义的其他函数,也应该先在头文件中声明,再调 用:
StudentManager.h
//欢迎信息
void welcome();
main函数
//创建头结点
Node* head = malloc(sizeof(Node)); head->next = NULL;welcome();
0-4 根据用户输入选择功能
通过_getch() 函数获取用户输入的单个字符,此函数不需要回车,并通过switch进行判断
welcome();
char c = _getch(); switch (c)
{case '1'://录入学生信息break; case '2'://打印学生信息break;case '3'://统计学生人数break; case '4'://查找学生信息break; case '5'://修改学生信息break;case '6'://删除学生信息break; case '7'://按成绩排序break; case '8'://退出系统break;default:break;
}
1. 录入学生信息
定义inputStudent 函数,实现新增学生信息的功能,在main函数中调用。
void inputStudent(Node* head) {//定义指针指向头结点,用于遍历链表Node* move = head;while (move->next != NULL) { move = move->next;}//创建结点Node* fresh = malloc(sizeof(Node)); fresh->next = NULL;//输入用户信息printf("请输入学生的学号、姓名、成绩: ");scanf("%d%s%d", &fresh->stu.stuNum, fresh->stu.name, &fresh->stu.score);//将新创建的结点,添加到链表的尾部move->next = fresh;
}
在main函数中调用
...case '1': //录入学生信息inputStudent(head); break;
...
1-0 实现循环录入
添加循环,将welcome 函数及switch判断都加入到循环体,保持程序一直运行,实现循环录入学生信息功能
int main() {//创建头结点Node* head = malloc(sizeof(Node));head->next = NULL;while (1) {welcome();/*等待从键盘接收一个字符, 输入字符后,回车才能录入,注意:回车符号会被存储至缓存区,下次循环导致获取到回车//char c = getchar();//getchar(); 吸收回车getch:不需要回车,直接获取输入的字符,需要包含conio.h头文件,2022之前的版本需要使用_getch()*/char c = _getch();switch (c) {case '1': //录入学生信息inputStudent(head);break;case '2': //打印学生信息break;case '3': //统计学生人数break;case '4': //查找学生信息break;case '5': //修改学生信息break;case '6': //删除学生信息break;case '7': //按成绩排序break;case '8': //退出系统break;default:break;}}return 0;
}
注意,如果使用的是 getchar 函数或 scanf 函数,会存在缓冲区的问题,每次输入数据后,回车都会被
保存到缓冲区,导致下次出现问题,需要再使用一次getchar吸收回车。
_getch 函数不需要回车就可以直接读取到数据,不存在此问题。
1-1 程序暂停和清空控制台
每次录入学生信息后,进入到下一次循环,会重新打印欢迎信息,可以先使程序暂停,等待用户下一步指示
system("pause");//按下任意键后,会继续执行程序
当用户按下任意键后,可以先清空控制台,防止重复出现欢迎信息
system("cls");
两者合用,加入到 inputStudent 函数中
void inputStudent(Node* head) {//定义指针指向头结点,用于遍历链表Node* move = head;......system("pause");system("cls");
}
2. 打印学生信息
定义 printStudent 函数,实现打印所有学生信息的功能
void printStudent(Node* head) {Node* move = head->next;while (move != NULL) {printf("学号:%d 姓名:%s 成绩:%d\n",move->stu.stuNum,move->stu.name,move->stu.score);move = move->next;}system("pause");system("cls");
}
在main函数中调用
...case '2': //打印学生信息printStudent(head);break;
...
3. 统计学生人数
定义 countStudent 函数,统计学生总人数
void countStudent(Node* head) {int count = 0;Node* move = head->next;while (move != NULL) {count++;move = move->next;}printf("学生总人数为:%d\n",count);system("pause");//清屏system("cls");
}
在main函数中调用
case '3': //统计学生人数countStudent(head);break;
4. 查找学生信息
定义 findStudent 函数,用于根据学号查找学生信息
void findStudent(Node* head) {int stuNum;printf("请输入要查找的学生的学号: ");scanf("%d", &stuNum);Node* move = head->next;while (move != NULL) {if (move->stu.stuNum = stuNum) {printf("学号:%d 姓名:%s 成绩:%d\n", move->stu.stuNum, move->stu.name,move->stu.score);system("pause");system("cls");return;}move = move->next;}printf("未查找到学生信息\n");system("pause");system("cls");
}
在main函数中调用
case '4': //查找学生信息findStudent(head);
4-0. 学生信息持久化
定义 saveStudent 函数,将链表数据保存至文件中
void saveStudent(Node* head) {//打开文件FILE* file = fopen("./stu.info", "w");if (file == NULL) {printf("打开文件失败\n");return;}Node* move = head->next;while (move != NULL) {//将结构体写入文件if (fwrite(&move->stu, sizeof(Student), 1, file) != 1) {printf("保存%s出现错误\n", move->stu.name);}move = move->next;}//关闭文件fclose(file);
}
在 inputStudent 函数中调用 saveStudent ,实现信息的保存
void inputStudent(Node* head) {//定义指针指向头结点,用于遍历链表Node* move = head;...//将学生信息保存至文件saveStudent(head);system("pause");system("cls");
}
定义 loadStudent 函数实现学生信息的读取
void loadStudent(Node* head) {//打开文件FILE* file = fopen("./stu.info", "r");if (!file) {printf("未找到学生文件,跳过读取\n");return;}//创建一个结点Node* fresh = malloc(sizeof(Node));fresh->next = NULL;Node* move = head;while (fread(&fresh->stu, sizeof(Student), 1, file) == 1) {move->next = fresh;move = fresh;fresh = malloc(sizeof(Node));fresh->next = NULL;}free(fresh); //最后多定义一个fresh,要将它释放掉//关闭文件fclose(file);printf("读取成功\n");
}
在main函数中调用 loadStudent ,每次程序启动时,先读取之前存储的学生信息
int main() {//创建头结点Node* head = malloc(sizeof(Node));head->next = NULL;//读取学生信息loadStudent(head);
}
5. 修改学生信息
定义 modifyStudent 函数,用于根据学生学号修改学生信息
void modifyStudent(Node* head) {printf("请输入需要修改的学生学号 ");int stuNum = 0;scanf("%d", &stuNum);Node* move = head;while (move != NULL) {if (move->stu.stuNum == stuNum) {printf("请输入学生姓名,成绩\n");scanf("%s%d",move->stu.name,&move->stu.score);printf("修改学生信息成功\n");//不再循环break;}move = move->next;}//如果循环完毕,也没找到学生if (move == NULL) {printf("未找到学生信息\n");}//同步到文件saveStudent(head);system("pause");system("cls");
}
在main函数中调用
case '5': //修改学生信息modifyStudent(head);break;
6. 删除学生信息
定义 deleteStudent 函数,用于根据学号删除学生
void deleteStudent(Node* head) {printf("请输入要删除的学生学号 ");int stuNum = 0;scanf("%d", &stuNum);Node* move = head;while (move->next != NULL) {if (stuNum == move->next->stu.stuNum) {Node* temp = move->next;move->next = move->next->next; //删除结点只需要一句free(temp); //最后记得将删除的动态空间释放掉temp = NULL; //释放后随即指向NULL//同步到文件saveStudent(head);printf("删除学生成功\n");break;}move = move->next;}if (move->next == NULL) {printf("未找到学生信息\n");}system("pause");system("cls");
}
7. 按成绩排序
定义 sortStudent 函数,使用冒泡排序,按成绩从小到大进行排序
void sortStudent(Node* head) {Node* save = NULL;Node* move = NULL;for (Node* turn = head->next; turn->next != NULL; turn = turn->next) {for (move = head->next; move->next != save; move = move->next) {if (move->stu.score > move->next->stu.score) {Student temp = move->stu;move->stu = move->next->stu;move->next->stu = temp;}}save = move;}printStudent(head);
}
在main函数中调用
case '7': //按成绩排序sortStudent(head);
8. 退出系统
case '8': //退出系统system("cls");printf("Bye Bye!\n");exit(0);
总结
学籍管理系统是一种基于单链表的学生管理系统,通过使用纯C语言实现。该系统具有多项功能,包括录入学生信息、打印学生信息、统计学生人数、查找学生信息、修改学生信息、删除学生信息以及按成绩排序。此外,该系统还具备学生信息持久化的功能,能够将学生数据保存至文件中,以便下次启动程序时自动读取。在录入学生信息功能中,用户可以逐个输入学生的学号、姓名和成绩。打印学生信息功能可以将系统中存储的所有学生信息依次输出。统计学生人数功能可以统计出当前系统中的学生总人数。查找学生信息功能可以根据学号查找学生的详细信息。修改学生信息功能可以根据学号修改学生的姓名和成绩。删除学生信息功能可以根据学号删除学生的信息。按成绩排序功能可以将学生信息按照成绩从小到大进行排序。最后,退出系统功能可以结束程序的运行。通过以上功能,学籍系统可以方便地管理学生的信息,并能够保证数据的持久化安全性。