初学者能够做出这个项目,想必计算机二级C语言也不在话下,我这个做出来供大家参考学习。
不至于说是毕业,但是能做出来也是说明你的水平很高了。
主要是对链表的运用以及数据的处理,内容包括对链表的增删减改,数据处理,文件对结构体的读写,用来当做练习C语言的项目是个很好的选择。
视频效果
视频最后读取后,不是删除后的结果的原因是:最后一次关闭之前没有点击保存。
易知的学生管理系统
下面是对项目的详细解释,最后末尾附上整个代码,直接复制粘贴到编译器,编译完直接启动就好。
1.头文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
标准库不用说,主要是输入和输出函数
内存分配函数要用到malloc,所以要使用stdlib.h
对于姓名这种字符串的处理,用string.h库会方便很多
系统函数cls清屏还有Sleep()延时等,都要用到系统函数,如果是vs,可能应该是sleep(),小写的
2.宏定义
typedef struct student {
char name[10];//姓名
int grade;//分数
struct student *next;
} STD;
STD *h, *q, *p, *t, *k; //把要用的指针定义好
FILE *fp;//创建文件指针
//后面通用的指针
对链表进行宏定义,指针也是。我知道规范写法不是这样,但是我这样写可以不用在每个函数都要再定义一遍,方便一些。
然后我也不想整那么麻烦,就字符串和整型的数据就够了,毕竟换别的大差不差。
这里的话就是可以让STD代表struct student,后续使用就方便很多写,比如每次申请内存时,malloc前面类型和后面的大小,都方便,这里听不懂后面会知道。
然后 我默认h是头指针,k是尾指针,p,q是遍历指针,t是申请内存的指针。
最后是文件指针,主要用来打开和关闭文件的。
3.菜单目录
void sign() {
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("8.查看全部成绩\n");
printf("9.保存成绩\n");
printf("10.读取成绩\n");
printf("11.退出系统\n");
}
这个没有什么,就是显示菜单的相关选项内容而已
4.文件保存
void save() {
system("cls");
if (h == NULL) {
printf("当前暂无学生名单!\n");
Sleep(1000);
return ;
}
fp = fopen("students.txt", "wb");//打开文件指针,创建名为students的.txt文件,wb代表二进制写入
p = h;
while (p != NULL) {
fwrite(p, sizeof(STD), 1, fp);//不断把链表写入
// p是链表,每次写入一个链表的大小,每次写入一次,写入的文件指针
p = p->next;
}
fclose(fp); //读完了要关掉
printf("保存成功");
Sleep(1000);
return ;
}
结构体一般是用fwrite来写入,我在注释里说得很清楚了,这里不过多解释。
逻辑是:先看看头节点是不是为空,有没有东西,如果没有,那么就退出。如果有,就打开文件指针fp打开这个.txt文件,wb为二进制读写的方式,因为其他方式容易出现整型数据错乱。后面就不断遍历,不断把结构体写入,最后关掉文件。
5.文件读取
void load() {
fp = fopen("students.txt", "rb");//打开文件指针,读取名为students的.txt文件,r代表二进制读取
if (fp == NULL) {
printf("读取失败!");
Sleep(1000);
return ;
}
while (1) {
t = (STD *)malloc(sizeof(STD)); // 为新节点分配内存
if (fread(t, sizeof(STD), 1, fp) != 1) { // 尝试读取数据
free(t); // 如果读取失败,释放内存
break; // 退出循环
}
if (h == NULL) {
h = t;
k = t; // 初始化头指针和尾指针
} else {
k->next = t; // 将新节点添加到链表末尾
k = t; // 更新尾指针
}
printf("%s %d\n", t->name, t->grade); // 打印读取的数据观察有没有问题
}
fclose(fp); //读完了要关掉
printf("读取成功");
Sleep(1000);
return ;
}
这个读取,注释里面已经讲得很清楚了,我就不多说,就主要说一下fread()函数。
这个函数读取完后返回的是数据量,读取结构体的情况下,一般返回1就代表结束了。
6.添加学生信息
void add() {
system("cls");
char sname[10];
int sgrade;//创建数据缓冲区
t = (STD *)malloc(sizeof(STD)); //创建一个链表大小的空间,并让t指向它
printf("***添加学生成绩***\n");
printf("清输入学生姓名:\n");
scanf("%s", sname);
sgrade = 101;
printf("清输入学生成绩:\n");
while (sgrade > 100 || sgrade < 0) {
scanf("%d", &sgrade);
if (sgrade > 100 || sgrade < 0)
printf("输入成绩有误,请重新输入!\n");
}
strcpy(t->name, sname);
t->grade = sgrade;
t->next = NULL;
if (h == NULL) {
h = t;
k = t; //如果没有创建,那么当前指针和尾指针都指向它
} else {
k->next = t; //生成的s添加到尾指针的下一个地址
k = t; //尾指针指向它
}
printf("添加成功!");
Sleep(1000);
return ;
}
这里相当于添加链表,注释也详细,这里就不说了。这里我不直接给结果体上的成员赋值,是因为我发现这样程序卡死,虽然我也不懂为什么。malloc是申请内存函数,前面的(STD*)是强转类型的意思,后面的sizeof(STD)求出一个STD结构体的大小,然后通过malloc函数申请出来。
7.删除学生信息
void dele() {
system("cls");
char sname[10];
if (h == NULL) {
printf("当前暂无学生名单!\n");
Sleep(1000);
return ;
}
printf("***删除学生成绩***\n");
printf("***当前名单***\n");
p = h;
while (p != NULL) {
printf("姓名:%s \t成绩:%3d\n", p->name, p->grade);
p = p->next;
}
printf("请输入要删除学生的名字\n");
scanf("%s", sname);
q = h;
if (strcmp(h->name, sname) == 0) {//查询直到名字匹配
h = h->next;
free(q);//如果是头结点,那么直接让头指向下一个,然后释放头结点
printf("删除成功!\n");
Sleep(1000);
return ;
} else {
//当不是头结点的时候
while (strcmp(q->name, sname) != 0) {
p = q;
q = q->next;
if (q == NULL) {
printf("输入有误,没有找到该学生的名字!\n");
Sleep(1000);
return ;
}
if (strcmp(q->name, sname) == 0) {
p->next = q->next; //直接跳过链接起来
if (q == k)
k = p->next; //如果是尾节点,那么需要指向上一个
free(q);
printf("删除成功!\n");
Sleep(1000);
return ;
}
}
}
}
这里注释也很清晰,不多说,删除链表麻烦些。逻辑是先判断要删除的是不是头结点,是的话就直接让头移动到下一个。如果是中间的那么就把要删除的上一个和下一个链接起来,释放掉中间的。同时观察是不是尾节点。
8.修改和查询学生信息
void change() {
system("cls");
if (h == NULL) {
printf("当前暂无学生名单!\n");
Sleep(1000);
return ;
}
printf("***修改学生成绩***\n");
printf("***当前名单***\n");
p = h;
while (p != NULL) {
printf("姓名:%s \t成绩:%3d\n", p->name, p->grade);
p = p->next;
}
printf("请输入要修成绩改学生的名字\n");
char sname[10];
scanf("%s", sname);
p = h;
while (p != NULL) {
if (strcmp(p->name, sname) == 0) {
printf("查找成功!\n");
printf("请输入要修成绩的数值\n");
scanf("%d", &p->grade);
printf("修改成功!");
Sleep(1000);
return ;
}
p = p->next;
}
}
void check() {
p = h;
system("cls");
if (h == NULL) {
printf("当前暂无学生名单!\n");
Sleep(1000);
return ;
}
printf("请输入要查询成绩的学生名字\n");
char sname[10];
scanf("%s", sname);
while (p != NULL) {
if (strcmp(p->name, sname) == 0) {
printf("查询成功!\n");
Sleep(1000);
printf("该学生\t姓名:%s\t成绩:%d\n", p->name, p->grade);
getchar();
getchar();
return ;
}
p = p->next;
if (p == NULL) {
printf("输入名字有误,请重新查询!\n");
Sleep(1000);
return ;
}
}
}
这里的话就没有加分数输入是否正确的检测,懂得查改这个功能就好了
9.大到小和小到大程序排序
void large() {
p = h;
q = h;
system("cls");
if (h == NULL) {
printf("当前暂无学生名单!\n");
Sleep(1000);
return ;
}
int sgrade, num = 0;
char sname[10];
p = h;
while (p != NULL) {
num++;
p = p->next;
}
while (num--) {
p = h;
q = p->next;
while (q != NULL) {
if (p->grade < q->grade) {
sgrade = p->grade;
strcpy(sname, p->name);
p->grade = q->grade;
strcpy(p->name, q->name);
q->grade = sgrade;
strcpy(q->name, sname);
}
p = p->next;
q = p->next;
}
}
printf("排序完成!");
Sleep(1000);
return ;
}
void miner() {
p = h;
q = h;
int sgrade, num = 0;
char sname[10];
system("cls");
if (h == NULL) {
printf("当前暂无学生名单!\n");
Sleep(1000);
return ;
}
while (p != NULL) {
num++;
p = p->next;
}
while (num--) {
p = h;
q = p->next;
while (q != NULL) {
if (p->grade > q->grade) {
sgrade = p->grade;
strcpy(sname, p->name);
p->grade = q->grade;
strcpy(p->name, q->name);
q->grade = sgrade;
strcpy(q->name, sname);
}
p = p->next;
q = p->next;
}
}
printf("排序完成!");
Sleep(1000);
return ;
}
这个是基本排序的方法,冒泡排序法,没什么好说的,只是对结构体排序,也要考虑*next,不能简单直接交换节点,容易导致信息错乱,然后字符串要用strcoy,不能简单赋值。
10.统计学生信息
void analysis() {
system("cls");
double ave;
int sum = 0, max = h->grade, min = h->grade, num = 0;
p = h;
while (p != NULL) {
num++;
sum += p->grade;
if (p->grade > max)
max = p->grade;
if (p->grade < min)
min = p->grade;
p = p->next;
}
ave = sum / (double)num;
p = h;
while (p != NULL) {
printf("姓名:%s \t成绩:%3d\n", p->name, p->grade);
p = p->next;
}
printf("\n\n总分为%d\n平均分为%.2f\n总人数为%d\n最高分为%d\n最低分为%d\n", sum, ave, num, max, min);
getchar();
getchar();
return ;
}
基本统计数据方法,也没有什么好说的
11.浏览学生信息
void look() {
p = h;
system("cls");
if (h == NULL) {
printf("当前暂无学生名单!\n");
Sleep(1000);
return ;
}
printf("***学生成绩***\n");
while (p != NULL) {
printf("姓名:%s \t成绩:%3d\n", p->name, p->grade);
p = p->next;
}
getchar();
getchar();
return ;
}
这个是对链表进行遍历,也没什么好说的
12.菜单选表以及主函数
void menu() {
int option;
while (1) {
system("cls");
sign();
scanf("%d", &option);
switch (option) {
case 1:
add();
break;
case 2:
dele();
break;
case 3:
change();
break;
case 4:
large();
break;
case 5:
miner();
break;
case 6:
check();
break;
case 7:
analysis();
break;
case 8:
look();
break;
case 9:
save();
break;
case 10:
load();
break;
case 11:
exit(1);
break;
}
}
}
int main() {
h = NULL;
menu();
return 0;
}
主要是菜单功能的写法,主要是switch配合无限循环的用法
12.全部代码---直接复制粘贴这里就可以运行整个程序了
不需要一个个模块的复制粘贴
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
typedef struct student {
char name[10];//姓名
int grade;//分数
struct student *next;
} STD;
STD *h, *q, *p, *t, *k; //把要用的指针定义好
FILE *fp;//创建文件指针
//后面通用的指针
void sign() {
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("8.查看全部成绩\n");
printf("9.保存成绩\n");
printf("10.读取成绩\n");
printf("11.退出系统\n");
}
void save() {
system("cls");
if (h == NULL) {
printf("当前暂无学生名单!\n");
Sleep(1000);
return ;
}
fp = fopen("students.txt", "wb");//打开文件指针,创建名为students的.txt文件,wb代表二进制写入
p = h;
while (p != NULL) {
fwrite(p, sizeof(STD), 1, fp);//不断把链表写入
// p是链表,每次写入一个链表的大小,每次写入一次,写入的文件指针
p = p->next;
}
fclose(fp); //读完了要关掉
printf("保存成功");
Sleep(1000);
return ;
}
void load() {
fp = fopen("students.txt", "rb");//打开文件指针,读取名为students的.txt文件,r代表二进制读取
if (fp == NULL) {
printf("读取失败!");
Sleep(1000);
return ;
}
while (1) {
t = (STD *)malloc(sizeof(STD)); // 为新节点分配内存
if (fread(t, sizeof(STD), 1, fp) != 1) { // 尝试读取数据
free(t); // 如果读取失败,释放内存
break; // 退出循环
}
if (h == NULL) {
h = t;
k = t; // 初始化头指针和尾指针
} else {
k->next = t; // 将新节点添加到链表末尾
k = t; // 更新尾指针
}
printf("%s %d\n", t->name, t->grade); // 打印读取的数据观察有没有问题
}
fclose(fp); //读完了要关掉
printf("读取成功");
Sleep(1000);
return ;
}
void add() {
system("cls");
char sname[10];
int sgrade;//创建数据缓冲区
t = (STD *)malloc(sizeof(STD)); //创建一个链表大小的空间,并让t指向它
printf("***添加学生成绩***\n");
printf("清输入学生姓名:\n");
scanf("%s", sname);
sgrade = 101;
printf("清输入学生成绩:\n");
while (sgrade > 100 || sgrade < 0) {
scanf("%d", &sgrade);
if (sgrade > 100 || sgrade < 0)
printf("输入成绩有误,请重新输入!\n");
}
strcpy(t->name, sname);
t->grade = sgrade;
t->next = NULL;
if (h == NULL) {
h = t;
k = t; //如果没有创建,那么当前指针和尾指针都指向它
} else {
k->next = t; //生成的s添加到尾指针的下一个地址
k = t; //尾指针指向它
}
printf("添加成功!");
Sleep(1000);
return ;
}
void dele() {
system("cls");
char sname[10];
if (h == NULL) {
printf("当前暂无学生名单!\n");
Sleep(1000);
return ;
}
printf("***删除学生成绩***\n");
printf("***当前名单***\n");
p = h;
while (p != NULL) {
printf("姓名:%s \t成绩:%3d\n", p->name, p->grade);
p = p->next;
}
printf("请输入要删除学生的名字\n");
scanf("%s", sname);
q = h;
if (strcmp(h->name, sname) == 0) {//查询直到名字匹配
h = h->next;
free(q);//如果是头结点,那么直接让头指向下一个,然后释放头结点
printf("删除成功!\n");
Sleep(1000);
return ;
} else {
//当不是头结点的时候
while (strcmp(q->name, sname) != 0) {
p = q;
q = q->next;
if (q == NULL) {
printf("输入有误,没有找到该学生的名字!\n");
Sleep(1000);
return ;
}
if (strcmp(q->name, sname) == 0) {
p->next = q->next; //直接跳过链接起来
if (q == k)
k = p->next; //如果是尾节点,那么需要指向上一个
free(q);
printf("删除成功!\n");
Sleep(1000);
return ;
}
}
}
}
void change() {
system("cls");
if (h == NULL) {
printf("当前暂无学生名单!\n");
Sleep(1000);
return ;
}
printf("***修改学生成绩***\n");
printf("***当前名单***\n");
p = h;
while (p != NULL) {
printf("姓名:%s \t成绩:%3d\n", p->name, p->grade);
p = p->next;
}
printf("请输入要修成绩改学生的名字\n");
char sname[10];
scanf("%s", sname);
p = h;
while (p != NULL) {
if (strcmp(p->name, sname) == 0) {
printf("查找成功!\n");
printf("请输入要修成绩的数值\n");
scanf("%d", &p->grade);
printf("修改成功!");
Sleep(1000);
return ;
}
p = p->next;
}
}
void large() {
p = h;
q = h;
system("cls");
if (h == NULL) {
printf("当前暂无学生名单!\n");
Sleep(1000);
return ;
}
int sgrade, num = 0;
char sname[10];
p = h;
while (p != NULL) {
num++;
p = p->next;
}
while (num--) {
p = h;
q = p->next;
while (q != NULL) {
if (p->grade < q->grade) {
sgrade = p->grade;
strcpy(sname, p->name);
p->grade = q->grade;
strcpy(p->name, q->name);
q->grade = sgrade;
strcpy(q->name, sname);
}
p = p->next;
q = p->next;
}
}
printf("排序完成!");
Sleep(1000);
return ;
}
void miner() {
p = h;
q = h;
int sgrade, num = 0;
char sname[10];
system("cls");
if (h == NULL) {
printf("当前暂无学生名单!\n");
Sleep(1000);
return ;
}
while (p != NULL) {
num++;
p = p->next;
}
while (num--) {
p = h;
q = p->next;
while (q != NULL) {
if (p->grade > q->grade) {
sgrade = p->grade;
strcpy(sname, p->name);
p->grade = q->grade;
strcpy(p->name, q->name);
q->grade = sgrade;
strcpy(q->name, sname);
}
p = p->next;
q = p->next;
}
}
printf("排序完成!");
Sleep(1000);
return ;
}
void check() {
p = h;
system("cls");
if (h == NULL) {
printf("当前暂无学生名单!\n");
Sleep(1000);
return ;
}
printf("请输入要查询成绩的学生名字\n");
char sname[10];
scanf("%s", sname);
while (p != NULL) {
if (strcmp(p->name, sname) == 0) {
printf("查询成功!\n");
Sleep(1000);
printf("该学生\t姓名:%s\t成绩:%d\n", p->name, p->grade);
getchar();
getchar();
return ;
}
p = p->next;
if (p == NULL) {
printf("输入名字有误,请重新查询!\n");
Sleep(1000);
return ;
}
}
}
void analysis() {
system("cls");
double ave;
int sum = 0, max = h->grade, min = h->grade, num = 0;
p = h;
while (p != NULL) {
num++;
sum += p->grade;
if (p->grade > max)
max = p->grade;
if (p->grade < min)
min = p->grade;
p = p->next;
}
ave = sum / (double)num;
p = h;
while (p != NULL) {
printf("姓名:%s \t成绩:%3d\n", p->name, p->grade);
p = p->next;
}
printf("\n\n总分为%d\n平均分为%.2f\n总人数为%d\n最高分为%d\n最低分为%d\n", sum, ave, num, max, min);
getchar();
getchar();
return ;
}
void look() {
p = h;
system("cls");
if (h == NULL) {
printf("当前暂无学生名单!\n");
Sleep(1000);
return ;
}
printf("***学生成绩***\n");
while (p != NULL) {
printf("姓名:%s \t成绩:%3d\n", p->name, p->grade);
p = p->next;
}
getchar();
getchar();
return ;
}
void menu() {
int option;
while (1) {
system("cls");
sign();
scanf("%d", &option);
switch (option) {
case 1:
add();
break;
case 2:
dele();
break;
case 3:
change();
break;
case 4:
large();
break;
case 5:
miner();
break;
case 6:
check();
break;
case 7:
analysis();
break;
case 8:
look();
break;
case 9:
save();
break;
case 10:
load();
break;
case 11:
exit(1);
break;
}
}
}
int main() {
h = NULL;
menu();
return 0;
}