普通实现学生管理系统
题目:
代码:
#include <myhead.h> int menu(int *p); struct student // 学生信息数据结构体 { char name[20]; float score; int age; }; /*************************打印学生信息***************/ int print_student(struct student s[], int *p) { if (*p == 0) { printf("\n当前没有学生信息,请先添加学生信息\n"); return 1; } printf("\n=================打印学生信息列表====================\n"); for (int i = 0; i < *p; i++) // 循环输出学生信息 { printf("|第%d个学生信息:姓名:%-5s;年龄:%-3d;分数:%-3.2f\n", i + 1, s[i].name, s[i].age, s[i].score); } printf("====================学生共计%d个====================\n", *p); } /************************返回函数**********************************/ int my_return(int *ret) { if (*ret == 0) { printf("\n终止当前操作;返回上一步操作\n"); return 1; } return 0; } /***********************枚举定义*************************************/ enum option { EXIT = 0, ADD, //=1 DELETE, //=2 MODIFY, //=3 FIND, //=4 SHOW }; /************************界面菜单**********************************************/ int menu(int *p) { static int select; start: printf("\n\t------------------------------------------\n"); printf("\t--------------学生信息管理系统-------------\n"); printf("\t--------------1、增添学生信息--------------\n"); printf("\t--------------2、删除学生信息--------------\n"); printf("\t--------------3、修改学生信息--------------\n"); printf("\t--------------4、查找学生信息--------------\n"); printf("\t--------------5、显示学生信息--------------\n"); printf("\t--------------0、 退出系统 --------------\n"); printf("\t-------------------------------------------\n\n"); printf("请输入你选择:\n"); if (scanf("%d", &select) != 1) { while (getchar() != '\n') ; // 清理输入缓冲区直到遇到换行符 printf("\n输入有误,请重新输入:\n"); goto start; } // 检查是否有未读取的字符(例如,用户输入了"1 "后的其他字符) int c = getchar(); if (c != '\n') { while (getchar() != '\n') ; // 如果有其他字符,继续清理缓冲区 printf("\n输入有误,请仅输入单个数字选项:\n"); goto start; } if (select >= 0 && select <= 5) { // 判断输入是否正确 if (*p == 0 && select != 1 && select != 0) { printf("\n还未添加学生信息,请添加学生信息!\n"); goto start; } return select; // 正确则返回输入的值 } else { printf("\n输入有误,请重新输入:\n"); goto start; } } /*****************************增添学生信息**********************************/ int add_stu(struct student s[], int *p) { static int num; // 添加学生个数 int *ret = # start: printf("\n请输入你要添加学生信息的个数:(输入‘0’返回上一步操作)\n"); if (scanf("%d", &num) != 1) { while (getchar() != '\n') ; printf("\n输入有误,请重新输入:\n"); goto start; } int c = getchar(); if (c != '\n') { while (getchar() != '\n') ; // 如果有其他字符,继续清理缓冲区 printf("\n输入有误,请仅输入单个数字选项:\n"); goto start; } if (num < 1 || num > 50) { if (num == 0) { if (my_return(ret)) // 判断是否返回上一步 { return 1; } } printf("\n输入的学生个数应为1到50之间的整数,请重新输入:\n"); goto start; } int j = 1; for (int i = *p; i < num + *p; i++) // 循环输入学生信息 { struct student temp; // 使用临时结构体存储新学生信息,确保信息完全正确后再复制到数组中 printf("\n请按顺序输入%d个学生信息:姓名,年龄,分数:\n", j++); scanf("%5s %3d %3f", temp.name, &temp.age, &temp.score); // 存入临时结构体中 if (strcmp(temp.name, " ") == 0 || temp.age < 10 || temp.age > 100 || temp.score < 0) // 判断输入是否有效 { printf("\n输入无效,请重新输入。\n"); i--; j--; int c; while ((c = getchar()) != '\n' && c != EOF);//清空缓存区 memset(&temp, 0, sizeof(temp));//清空临时结构体 continue; } // 确认无误后存入结构体 strcpy(s[i].name, temp.name); s[i].age = temp.age; s[i].score = temp.score; } printf("\n=================打印学生信息列表====================\n"); for (int i = 0; i < num + *p; i++) // 循环输出学生信息 { printf("|姓名:%-5s; 年龄:%-3d; 分数:%-3.2f \n", s[i].name, s[i].age, s[i].score); } *p += num; // 添加后的总人数重新赋值 printf("====================学生共计%d个====================\n", *p); } /**************************删除学生信息***********************************/ int delete_stu(struct student s[], int *p) { static int sub_num; // 学生下标 int *ret = &sub_num; start: printf("\n请输入你要删除第几个学生:(输入‘0’返回上一步操作)\n"); if (scanf("%d", &sub_num) != 1) { while (getchar() != '\n') ; // 清理输入缓冲区直到遇到换行符 printf("\n输入有误,请重新输入:\n"); goto start; } int c = getchar(); if (c != '\n') { while (getchar() != '\n') ; // 如果有其他字符,继续清理缓冲区 printf("\n输入有误,请仅输入单个数字选项:\n"); goto start; } if (my_return(ret)) // 判断是否返回上一步 { return 1; } if (sub_num <= *p && sub_num > 0) { for (int i = sub_num - 1; i < *p; i++) // 循环覆盖学生信息 { s[i] = s[i + 1]; } printf("\n删除成功。\n"); } else { printf("\n输入有误,请重新输入:\n"); goto start; } *p -= 1; printf("\n=================打印学生信息列表====================\n"); for (int i = 0; i < *p; i++) // 循环输出学生信息 { printf("|姓名:%-5s; 年龄:%-3d; 分数:%-3.2f \n", s[i].name, s[i].age, s[i].score); } printf("====================学生共计%d个====================\n", *p); } /**************************修改学生信息***********************************/ int modify_stu(struct student s[], int *p) { static int sub_num; // 学生下标 int *ret = &sub_num; start: printf("\n请输入你要修改第几个学生:(输入‘0’返回上一步操作)\n"); scanf("%d", &sub_num); if (sub_num > *p || *p == 0 || sub_num == 0) // 判断输入是否正确 { if (my_return(ret)) // 判断是否返回上一步 { return 1; } printf("\n输入有误,请重新输入:\n"); while (getchar() != '\n') ; // 吸收垃圾字符,防止死循环 goto start; } int c = getchar(); if (c != '\n') { while (getchar() != '\n') ; // 如果有其他字符,继续清理缓冲区 printf("\n输入有误,请仅输入单个数字选项:\n"); goto start; } printf("\n请输入修改后的姓名,年龄,分数。\n"); scanf("%s %d %f", s[sub_num - 1].name, &s[sub_num - 1].age, &s[sub_num - 1].score); printf("\n=================打印学生信息列表====================\n"); for (int i = 0; i < *p; i++) // 循环输出学生信息 { printf("|姓名:%-5s; 年龄:%-3d; 分数:%-3.2f \n", s[i].name, s[i].age, s[i].score); } printf("====================学生共计%d个====================\n", *p); } /**************************查找学生信息***********************************/ int search_stu(struct student s[], int *p) { static int sub_num; // 学生序号 int *ret = &sub_num; start: printf("\n请输入你要查找的学生序号:(输入‘0’返回上一步操作)\n"); scanf("%d", &sub_num); if (sub_num > *p || sub_num <= 0 || sub_num == 0) // 判断输入是否正确 { if (my_return(ret)) // 判断是否返回上一步 { return 1; } printf("\n输入有误,请重新输入:\n"); while (getchar() != '\n') ; // 吸收垃圾字符,防止死循环 goto start; } int c = getchar(); if (c != '\n') { while (getchar() != '\n') ; // 如果有其他字符,继续清理缓冲区 printf("\n输入有误,请仅输入单个数字选项:\n"); goto start; } printf("\n该学生信息为:\n"); printf("______________________________________\n"); printf("|姓名:%-5s; 年龄:%-3d; 分数:%-3.2f|\n", s[sub_num - 1].name, s[sub_num - 1].age, s[sub_num - 1].score); printf("——————————————————————————————————————\n"); } /**************************申请空间************************************/ struct student *my_malloc() { struct student *p = (struct student *)malloc(sizeof(struct student) * 50); if (p != NULL) // 判断是否申请成功 { return p; } else { perror("malloc:"); return NULL; } } /**********************主函数****************************/ int main(int argc, const char *argv[]) { struct student *ptr = my_malloc(); int sum = 0; int *p = ∑ while (1) { int select = menu(p); switch (select) { case EXIT: printf("\n已退出系统\n"); return 0; case ADD: // add_stu(ptr, p); if (add_stu(ptr, p)) { continue; } break; case DELETE: if (delete_stu(ptr, p)) { continue; } break; case MODIFY: if (modify_stu(ptr, p)) { continue; } break; case FIND: if (search_stu(ptr, p)) { continue; } break; case SHOW: print_student(ptr, p); break; } } free(ptr); ptr = NULL; return 0; }
输出:
(太多放不下,复制代码自己试试)。
- 遇到问题
在遇到吸收垃圾字符时,只能吸收一个的问题,第二个字符会被下一下scanf吸收,如果输入字符,则会出现死循环。
在上述代码 menu函数 中举例:
原代码:
scanf("%d", &select); if (select >= 0 && select <= 4) // 判断输入是否正确 { return select; // 正确则返回输入的值 } else { printf("输入有误,请重新输入:\n"); getchar(); // 吸收垃圾字符,防止死循环 goto start; }
输出:
改进后代码:
解决了不能吸收多个垃圾字符的情况:if (scanf("%d", &select) != 1) { while (getchar() != '\n') ; // 清理输入缓冲区直到遇到换行符 printf("输入有误,请重新输入:\n"); goto start; } if (select >= 0 && select <= 4) { // 判断输入是否正确 return select; // 正确则返回输入的值 } else { printf("输入有误,请重新输入:\n"); // 无需单独调用 getchar(),上面的清理逻辑已足够 goto start; }
输出:
添加枚举到switch中增加代码可读性:
enum option{ EXIT=0, ADD, DELETE, MODIFY, FIND } ; ………………………… switch (select) { case EXIT: printf("已退出系统\n"); return 0; case ADD: add_stu(ptr, p); break; case DELETE: delete_stu(ptr, p); break; case MODIFY: modify_stu(ptr, p); break; case FIND: search_stu(ptr, p); break; }
适当使用占位符使输出更加美观:
printf("|姓名:%-5s; 年龄:%-3d; 分数:%-3.2f \n", s[i].name, s[i].age, s[i].score);
在未添加学生信息时仍可以进入其它选项,不符合常识:
设置判断学生数为0 的时候不能进去其它选项
if (select >= 0 && select <= 4) { // 判断输入是否正确 if(*p == 0 && select != 1 && select != 0) { printf("\n还未添加学生信息,请添加学生信息!\n"); goto start; } return select; // 正确则返回输入的值 }
在输入选项时。输入“ 1 11”,下一个scanf会读取“11”,导致达不到想要的效果:
如:
if (scanf("%d", &select) != 1) { while (getchar() != '\n') ; // 清理输入缓冲区直到遇到换行符 printf("\n输入有误,请重新输入:\n"); goto start; } if (select >= 0 && select <= 4) { // 判断输入是否正确 return select; // 正确则返回输入的值 } else { printf("\n输入有误,请重新输入:\n"); // 无需单独调用 getchar(),上面的清理逻辑已足够 goto start; }
错误输出:
修改后代码:
if (scanf("%d", &select) != 1) { while (getchar() != '\n'); // 清理输入缓冲区直到遇到换行符 printf("\n输入有误,请重新输入:\n"); goto start; } // 检查是否有未读取的字符(例如,用户输入了"1 "后的其他字符) int c = getchar(); if (c != '\n') { while (getchar() != '\n'); // 如果有其他字符,继续清理缓冲区 printf("\n输入有误,请仅输入单个数字选项:\n"); goto start; }
输出:
输入学生信息错误数据后直接赋值:
printf("\n请按顺序输入:姓名,年龄,分数。\n"); for (int i = *p; i < num + *p; i++) // 循环输入学生信息 { scanf("%5s %3d %3f", s[i].name, &s[i].age, &s[i].score); }
修改后代码:
新建一个结构体,输入正确后再赋值到原结构体
for (int i = *p; i < num + *p; i++) // 循环输入学生信息 { struct student temp; // 使用临时结构体存储新学生信息,确保信息完全正确后再复制到数组中 // int flag = 0;//标记输入是否有效 printf("\n请按顺序输入:姓名,年龄,分数。\n"); scanf("%5s %3d %3f", temp.name, &temp.age, &temp.score); // 存入临时结构体中 if (strcmp(temp.name, "") == 0 || temp.age < 10 || temp.age > 100 || temp.score < 0) // 判断输入是否有效 { printf("\n输入无效,请重新输入。\n"); i--; continue; } // 确认无误后存入结构体 strcpy(s[i].name, temp.name); s[i].age = temp.age; s[i].score = temp.score; }
重新添加“返回上一步操作”,使界面更加人性化:
首先,我们让
my_return
函数返回一个布尔值,表示是否需要返回菜单。为了保持与原有逻辑的一致性,我们可以在调用者函数中根据这个返回值决定是否重新调用menu
。int my_return(int *ret) { if (*ret == 0) { printf("\n终止当前操作;返回上一步操作\n"); return 1; } return 0; }
调整
add_stu
函数以处理my_return
的返回值接下来,在
add_stu
函数中,根据my_return
的返回值决定是否重新调用menu
。这里,我们需要在add_stu
函数中包含对menu
的调用逻辑,以替代原来my_return
直接调用menu
的行为。int add_stu(struct student s[], int *p) { ..... if (my_return(&num, p)) // 如果需要返回到menu { return 1; // 从add_stu返回,由main函数处理是否重新调用menu } ..... }
main
函数处理add_stu
的返回最后,需要在
main
函数中处理add_stu
的返回值,根据返回值决定是否重新调用menu
。...... case ADD: if(add_stu(ptr, p)) { // 如果add_stu返回1,表示需要重新显示菜单,因此跳过switch-case直接重新开始循环 continue; } break; ......
ps:
此代码没有解决内存溢出问题,在添加学生信息时,可能会造成溢出。(最好利用define定义姓名最大长度);
修改学生信息时,只能全部修改,不能单独修改某一项的信息。
函数运用多个goto 语句导致程序不稳定,老板可能看不惯,慎用。
利用顺序表和分文件编译完成
主函数:
#include "sequence.h" int main(int argc, const char *argv[]) { sequenceListPtr P = seq_creat(); while (1) { int option = menu(P); switch (option) { case EXIT: printf("系统已退出!\n"); return 0; break; case ADD: seq_add(P); break; case DELETE: seq_del(P); break; case MODIFY: seq_modify(P); break; case FIND: seq_find_data(P); break; case SHOW: seq_show(P); break; case INSERT: seq_ins(P); break; case REMOVE_DUPLICATE: seq_remove_duplicate(P); break; case FIND_SUB: seq_find_sub(P); break; } } seq_free(P); return 0; }
源文件.c:
#include "sequence.h" int menu(sequenceListPtr S) { static int select; start: printf("\n\t------------------------------------------\n"); printf("\t--------------学生信息管理系统-------------\n"); printf("\t--------------1、增添学生信息--------------\n"); printf("\t--------------2、删除学生信息--------------\n"); printf("\t--------------3、修改学生信息--------------\n"); printf("\t--------------4、查找学生信息--------------\n"); printf("\t--------------5、打印学生信息--------------\n"); printf("\t--------------6、插入学生信息--------------\n"); printf("\t--------------7、学生信息去重--------------\n"); printf("\t--------------8、查找学生序号--------------\n"); printf("\t--------------0、 退出系统 --------------\n"); printf("\t-------------------------------------------\n\n"); printf("请输入你选择:\n"); if (scanf("%d", &select) != 1) { while (getchar() != '\n') ; // 清理输入缓冲区直到遇到换行符 printf("\n输入有误,请重新输入:\n"); goto start; } // 检查是否有未读取的字符(例如,用户输入了"1 "后的其他字符) int c = getchar(); if (c != '\n') { while (getchar() != '\n') ; // 如果有其他字符,继续清理缓冲区 printf("\n输入有误,请仅输入单个数字选项:\n"); goto start; } if (select >= 0 && select <= 8) { // 判断输入是否正确 if (S->len == 0 && select != 1 && select != 0) { printf("\n还未添加学生信息,请添加学生信息!\n"); goto start; } return select; // 正确则返回输入的值 } else { printf("\n输入有误,请重新输入:\n"); goto start; } } sequenceListPtr seq_creat() // 创建链表 { sequenceListPtr P = (sequenceListPtr)malloc(sizeof(sequenceList)); if (P == NULL) { perror("malloc:"); return NULL; } else { printf("链表创建成功!\n"); } memset(P, 0, sizeof(sequenceList)); // 整体清空 // memset(p->list,0,sizeof(p->list));//逐个清空 // p->len=0; return P; } int seq_fill(sequenceListPtr S) // 链表判满 { if (S == NULL) { printf("链表判满失败 !\n"); } return S->len == MAX; } int seq_empty(sequenceListPtr S) // 链表判空 { if (S == NULL) { printf("链表判空失败 !\n"); } return S->len == 0; } int seq_add(sequenceListPtr S) // 添加链表信息 { if (S == NULL || seq_fill(S)) { printf("添加失败!\n"); return -1; } int num; printf("请输入要添加的数据个数:\n"); if (scanf("%d", &num) == 1 && num > 0) { for (int i = 0; i < num; i++) { printf("请输入%d个学生姓名:\n", i + 1); scanf("%s", S->date[S->len].name); printf("请输入%d个学生分数:\n", i + 1); scanf("%f", &S->date[S->len].score); printf("请输入%d个学生学号:\n", i + 1); scanf("%d", &S->date[S->len].id); S->len++; } } return 1; } void seq_show(sequenceListPtr S) // 打印数据 { if (S == NULL || seq_empty(S)) { printf("打印失败!\n"); return; } printf("\n链表数据为:\n"); printf("姓名\t分数\t学号\n"); for (int i = 0; i < S->len; i++) { printf("%s\t%.2f\t%d\n", S->date[i].name, S->date[i].score, S->date[i].id); } printf("\n数据长度len=%d\n", S->len); return; } int seq_ins(sequenceListPtr S) // 插入数据 { int loc, i; printf("请输入要插入的位置:\n"); scanf("%d", &loc); if (S == NULL || seq_fill(S) || loc > MAX || loc < 0) { printf("插入失败!\n"); return -1; } for (i = S->len; i > loc - 1; i--) // 把要插入的位置后面的数据后移 { S->date[i] = S->date[i - 1]; } printf("请输入要插入的数据:\n"); printf("请输入姓名:\n"); scanf("%s", S->date[loc - 1].name); printf("请输入分数:\n"); scanf("%f", &S->date[loc - 1].score); printf("请输入学号:\n"); scanf("%d", &S->date[loc - 1].id); S->len++; return 1; } int seq_del(sequenceListPtr S) // 删除数据 { int loc, i; printf("请输入要删除的位置:\n"); scanf("%d", &loc); if (S == NULL || seq_empty(S) || loc >= S->len || loc <= 0) // 判断条件 { printf("删除失败!\n"); return -1; } for (int i = loc; i < S->len; i++) // 删除数据 { S->date[i - 1] = S->date[i]; } // printf("\n删除数据成功!\n"); S->len--; return 1; } int seq_find_sub(sequenceListPtr S) // 查找数据位置 { int loc, i; printf("请输入要查找的位置:\n"); scanf("%d", &loc); if (S == NULL || seq_empty(S) || loc > S->len || loc <= 0) // 判断条件 { printf("查找失败!\n"); return -1; } printf("\n查找的数据为date[%d]:姓名:%s\t分数:%.2f\t学号:%d\n", loc, S->date[loc - 1].name, S->date[loc - 1].score, S->date[loc - 1].id); return 1; } int seq_find_data(sequenceListPtr S) // 查找数据值 { int i, flag = 0; char loc[20]; printf("请输入要查找的姓名:\n"); scanf("%19s", loc); if (S == NULL || seq_empty(S)) // 判断条件 { printf("查找失败!\n"); return -1; } for (i = 0; i < S->len; i++) { if (strcmp(S->date[i].name, loc) == 0) { printf("该学生下标为%d\n", i); flag = 1; } } if (flag == 0) { printf("未找到该元素\n"); } return 1; } int seq_modify(sequenceListPtr S) // 修改数据 { int loc, i; printf("请输入要修改的位置:\n"); scanf("%d", &loc); if (S == NULL || seq_empty(S) || loc > S->len || loc <= 0) // 判断条件 { printf("修改失败!\n"); return -1; } printf("请输入姓名:\n"); scanf("%s", S->date[loc - 1].name); printf("请输入分数:\n"); scanf("%f", &S->date[loc - 1].score); printf("请输入学号:\n"); scanf("%d", &S->date[loc - 1].id); return 1; } int seq_remove_duplicate(sequenceListPtr S) // 删除重复姓名数据 { if (S == NULL || seq_empty(S)) // 判断条件 { printf("去重失败!\n"); return -1; } for (int i = 0; i < S->len; i++) { for (int j = i + 1; j < S->len; j++) { if (strcmp(S->date[i].name, S->date[j].name)==0) { for (int z = i+1; z < S->len; z++) // 删除数据 { S->date[z - 1] = S->date[z]; } // printf("\n删除数据成功!\n"); S->len--; j--; } } } } int seq_free(sequenceListPtr S) // 释放空间 { if (S == NULL) { printf("释放空间失败!\n"); return -1; } free(S); S = NULL; }
源文件.h
#ifndef __SEQUENCE_H__ #define __SEQUENCE_H__ #include <myhead.h> #define MAX 30 // 定义最大长度 #define MAX_name 10 //学生姓名长度 enum option { EXIT = 0, ADD, //1、增添学生信息 DELETE, //2、删除学生信息 MODIFY, //3、修改学生信息 FIND, //4、查找学生信息 SHOW,//5、打印学生信息 INSERT,//6、插入学生信息 REMOVE_DUPLICATE,//7、学生信息去重 FIND_SUB,//8、查找学生序号 }; typedef struct student { char name[MAX_name];//姓名 float score;//分数 int id;//学号 }DateTyp; typedef struct sequence { DateTyp date[MAX]; int len; } sequenceList, *sequenceListPtr; // 宏定义结构体 sequenceListPtr seq_creat(); // 链表创建 int seq_fill(sequenceListPtr S); // 链表判满 int seq_empty(sequenceListPtr S); // 链表判空 /*******增 删 改 查 均标号从1开始********/ int seq_add(sequenceListPtr S); // 添加数据 void seq_show(sequenceListPtr S); // 打印链表信息 int seq_ins(sequenceListPtr S); // 插入数据 int seq_del(sequenceListPtr S); // 删除数据 int seq_find_sub(sequenceListPtr S); // 查找数据下标 int seq_find_data(sequenceListPtr S); // 查找数据值 int seq_modify(sequenceListPtr S); // 修改数据 int seq_remove_duplicate(sequenceListPtr S); // 删除重复数据 int seq_free(sequenceListPtr S); // 释放空间 int menu(sequenceListPtr S);//菜单 #endif