完成了所有自定义头文件
的编写
完成程序主入口,一一对应功能实现算法,不断调直到所有功能基本上正常。
GitHub:https://github.com/ITchujian/StudentManagementSystem_2022_C
注:为方便分享本次开发的经验,我将把分析过程以及代码书写过程,以文字、图片形式合计放于开发记录中,但是一些非常基础的行为动作我将不会讲解或者阐述。
当前位置:【1.6 C案例】请君与我用C语言写一个千行的学生管理系统
可跳转:
引用自定义头文件,sysbrowse.h
,sysdoc.h
,sysmodify.h
。
定义main主函数,在主函数中,先加载系统设置文件config.bin
:
loadConfig();
定义一个线性表类型的变量:
SqList student_list;
然后将其初始化:
InitList(&student_list);
加载线性表配置list_path.bin
,最后加载学生信息students.bin
:
loadList(&student_list); loadStu(&student_list);
根据程序的功能结构:
利用do~while()
与switch~case~default
实现功能的选择,如下:
do { int home_select; scanf("%d", &home_select); switch (home_select) { case 1: { // 学生信息浏览系统 break; } case 2: { // 学生信息修改系统 break; } case 3: { // 保存信息 break; } case 4: { // 系统设置 break; } case 0: // 退出程序 return 0; default: // 判定其他的选择 break; } } while (TRUE);
在Switch语句前,我需要编写一个在主页显示的菜单:
Status homeMenu(void) { printf("┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n"); printf("┃ 学 生 信 息 管 理 系 统 ┃\n"); printf("┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫\n"); printf("┃请选择操作: ┃\n"); printf("┃ 1 > 学生信息浏览系统 ┃\n"); printf("┃ 2 > 学生信息修改系统 ┃\n"); printf("┃ 3 > [重要]保存信息 ┃\n"); printf("┃ 4 > 系 统 设 置 ┃\n"); printf("┃ 0 > 退 出 系 统 ┃\n"); printf("┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n"); printf("\nINPUT:"); return OK; }
然后,在Switch语句前添加:
system("cls"); // 为了每次循环后对之前的操作进行清屏 homeMenu();
学生信息浏览系统中,我们将其分为5个部分:
同理制作一个选择器,0返回主页的菜单选择界面。
并将sysbrowse.h
与kernel_list.h
中的相关方法一一部署,如下展示:
Status browseStuSystem(SqList* L) { do { system("cls"); browseMenu(); int browse_select; scanf("%d", &browse_select); switch (browse_select) { case 1: { putAllList(L); system("pause"); break; } case 2: { int student_id; printf("若学号不存在,则返回null,表示未找到\n"); printf("INPUT:"); scanf("%d", &student_id); ElemType* e = (ElemType*)malloc(sizeof(ElemType)); if (e == NULL) exit(INFEASIBLE); Status re = SearchElem(L, student_id, e, 0); if (re == INFEASIBLE) { printf("null\n"); system("pause"); break; } putSingleList(e); free(e); system("pause"); break; } case 3: { int sort_manner; char sort_key; printf("排序方式(0或任意负数为降序、1或任意正数为升序)\n"); printf("INPUT:"); scanf("%d", &sort_manner); getchar(); printf("\n排序项(n-学号、l-语文、m-数学、e-英语、a-平均分、s-总分)\n"); printf("INPUT:"); scanf("%c", &sort_key); ListSort(L, sort_manner, sort_key); putAllList(L); system("pause"); break; } case 4: { int sort_manner, i; char sort_key; ElemType* e = (ElemType*)malloc(sizeof(ElemType)); getchar(); printf("查询科目(l-语文、m-数学、e-英语、a-平均分、s-总分)\n"); printf("INPUT:"); scanf("%c", &sort_key); printf("正排名还是倒排名(0或任意负数为正排名、1或任意正数为倒排名)\n"); printf("INPUT:"); scanf("%d", &sort_manner); printf("第几名?\n"); printf("INPUT:"); scanf("%d", &i); rankSearch(L, sort_manner, sort_key, i, e); free(e); system("pause"); break; } case 5: { printf("输入0则以默认方式统计[科目分数60及格、总分180分及格],非0则为自定义统计\n"); printf("INPUT:"); int select; char comp_item[5][10] = { "语文", "数学", "英语", "平均分", "总分" }; float comp_score[5] = { 60, 60, 60, 60, 180 }; scanf("%d", &select); if (select == 0) statistic(L, comp_score); else { for (int i = 0; i < 5; i++) { printf("%s:", comp_item[i]); scanf("%f", &comp_score[i]); } statistic(L, comp_score); } system("pause"); break; } case 0: return OK; default: printf("该按键未开发任何功能,请重新输入\n"); break; } } while (TRUE); }
之后在主函数的选择器case 1
中调用这个函数即可。
与步骤4同一思路,对代码稍加修改即可。
其中,学生信息修改系统
的代码如下:
Status changeStuSystem(SqList* L) { do { system("cls"); changeMenu(); int change_select; scanf("%d", &change_select); switch (change_select) { case 1: { if (addStudent(L)) printf("添加成功!\n"); else printf("添加失败!\n"); system("pause"); break; } case 2: { int student_id; printf("若学号不存在,则修改失败\n"); printf("INPUT:"); scanf("%d", &student_id); if (changeElem(L, student_id) != INFEASIBLE) printf("修改成功!\n"); else printf("修改失败!\n"); system("pause"); break; } case 3: { int student_id; printf("若学号不存在,则删除失败\n"); printf("INPUT:"); scanf("%d", &student_id); deleteElem(L, student_id); system("pause"); break; } case 4: { resetAll(L); system("pause"); break; } case 0: return OK; default: printf("该按键未开发任何功能,请重新输入\n"); break; } } while (TRUE); }
保存文件
的代码如下:
Status ioFileStuSystem(SqList* L) { getchar(); char is_yes; printf("请输入y或Y确认:"); scanf("%c", &is_yes); if (is_yes == 'y' || is_yes == 'Y') { writeFile(config_bin.list_path, L, sizeof(SqList), 1); writeFile(config_bin.file_path, L->elem, sizeof(ElemType), L->length); } else return INFEASIBLE; system("pause"); return OK; }
系统设置
的代码如下:
Status setSystem(SqList* L) { do { system("cls"); setMenu(); int set_select; scanf("%d", &set_select); switch (set_select) { case 1: { char dir_path[] = ".\\SMSdir"; createFolder(dir_path); char config_path[128]; sprintf(config_path, "%s\\config.bin", dir_path); printf("\n学生信息表存储路径(默认是.\\SMSdir\\students.bin):"); scanf("%s", config_bin.file_path); writeFile(config_path, &config_bin, sizeof(SysConfig), 4); break; } case 2: { char dir_path[] = ".\\SMSdir"; createFolder(dir_path); char config_path[128]; sprintf(config_path, "%s\\config.bin", dir_path); printf("\n学生信息表备份路径(默认是.\\SMSdir\\students.bin.bak):"); scanf("%s", config_bin.backup_path); writeFile(config_path, &config_bin, sizeof(SysConfig), 4); break; } case 3: { char dir_path[] = ".\\SMSdir"; createFolder(dir_path); char config_path[128]; sprintf(config_path, "%s\\config.bin", dir_path); colorMenu(); char color[4]; printf("\n第一个表示背景色 第二个表示字体颜色 如06或者1A\n"); printf("INPUT:"); scanf("%s", color); sprintf(config_bin.sys_color, "color %s", color); writeFile(config_path, &config_bin, sizeof(SysConfig), 4); break; } case 4: { char dir_path[] = ".\\SMSdir"; createFolder(dir_path); char config_path[128]; sprintf(config_path, "%s\\config.bin", dir_path); printf("\n表格内核路径(默认是.\\SMSdir\\list_path.bin):"); scanf("%s", config_bin.list_path); writeFile(config_path, &config_bin, sizeof(SysConfig), 4); break; } case 0: return OK; default: printf("该按键未开发任何功能,请重新输入\n"); break; } } while (TRUE); }
分别对应主函数的选择开关部署这些功能函数。
我们的代码到此貌似已经写完了,接下来就是调试程序。
编译——直接报错,我有点懵了,报错信息如下:
C4996 ‘scanf’: This function or variable may be unsafe. Consider using scanf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details. StuMS E:\c_program\StuMS\StuMS\SMS_2022.cpp 44
看到这里,我算是想起来了,VS中使用scanf()
函数会报错,scanf()
在读取时不检查边界,所以可能会造成内存泄露,所以VS提供了scanf_s()
来替代,但是这里我们这样应付它:
然后在最后一行,添加上一句_CRT_SECURE_NO_WARNINGS
。
接下来,再次编译,成功!
然后,运行一下,一切正常,然后退出一下,发现,卡住了:
![在这里插入图片描述](https://www.www.zyiz.net/i/ll/?i=eb7866b754b64f3685e8a1f7f58e1c9b.png?,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Yid6KeB5bCP6I-cRw==,size_20,color_FFFFFF,t_70,g_se,x_16
啊这,咱也不知道为啥,调试一下吧,暂且先无断点调试!
错误:
读取某一位置时发生访问冲突
这是为啥呢?毕竟刚学完C语言没多久,第一次遭遇这个问题,思前想后的,猜测一波,访问冲突了,那该地址不会被其它程序使用了导致不可访问吧?那也不对啊,kernel.h
头文件中,线性表初始化肯定没有问题的:
Status InitList(SqList* L) { L->elem = (ElemType*)malloc(LIST_INIT_SIZE * sizeof(ElemType)); if (!(L->elem)) exit(OVERFLOWED); L->length = 0; L->list_size = LIST_INIT_SIZE; return OK; }
假设,我们在初始化就分配内存空间失败了,那就导致后面使用student_list
这个结构体变量也会直接报错。
而我们却是在选择器中输入0遭遇的,所以这段函数是没有问题的。
然后我们去掉InitList
后的加载配置,添加两个学生,然后回到主菜单保存信息,信息保存成功了。
再次恢复原来的代码,运行,发现了这个问题:
不服气的我,再度运行了多次,结果我电脑上的卡巴斯基是这样提醒我的:
大约500多个感染对象,我寻思,这内存访问了不该访问的?挺吓人的这个C语言,危险之处体现的淋漓尽致,怪我太菜了