一、需求分析:
1.设计题目:学生成绩管理系统
2.系统功能需求分析
这个学生成绩管理系统旨在帮助学校或教育机构有效管理学生的基本信息和成绩数据。系统具有以下主要功能:
(1)增添学生成绩:允许管理员录入学生的学号、姓名、年龄以及高等数学、大学英语、程序设计成绩。系统会自动计算并存储学生的总成绩和平均成绩。
(2)删除学生成绩:管理员可以根据学生的学号删除特定学生的信息,确保数据库中信息的完整性和准确性。
(3)修改学生成绩:支持管理员对学生的成绩信息进行修改,包括更新高等数学、大学英语和程序设计成绩,系统会实时重新计算总分和平均分。
(4)查询学生成绩:提供多种查询方式,可以根据学号或姓名查找特定学生的详细信息,包括学号、姓名、年龄以及各科成绩的具体数据和总体表现。
(5)按总成绩排名:系统可以根据学生的总成绩进行降序排列,展示所有学生的排名情况和详细成绩信息。
(6)系统退出:提供安全退出选项,确保数据的完整性和安全性。
二、概要设计:
系统总体设计框架流程示意图
文字说明:
包括具体的业务处理功能如下:
增添学生成绩:管理员录入学生信息和各科成绩,系统自动计算总成绩和平均成绩并存储。
删除学生成绩:根据输入的学号删除特定学生的信息,保证数据库中信息的完整性。
修改学生成绩:根据输入的学号删除特定学生的信息,允许管理员更新学生的各科成绩,系统实时重新计算总分和平均分。
查询学生成绩:支持按学号或姓名两种方式查询学生信息,显示学生的详细成绩和总体表现。
按总成绩排名:根据学生的总成绩降序排列,展示所有学生的排名和详细成绩信息(各科成绩,总分,平均分)。
图一 学生成绩管理系统整体设计框架图
三、详细设计:
学生成绩管理系统的算法设计思路包含以下主要功能模块:增添学生成绩、删除学生成绩、修改学生成绩、查询学生成绩、按总成绩排名、系统退出。每个模块都有其独特的设计和实现方式,具体如下:
1. 增添学生成绩
算法设计思路:
接收用户输入的学生信息,包括学号、姓名、年龄、高等数学成绩、大学英语成绩、程序设计成绩。
系统使用 scanf函数获取这些输入,并将其存储在全局结构体数组 s 的当前索引位置。
计算学生的总成绩和平均成绩:总成绩是各科成绩之和,平均成绩是总成绩除以科目数。
将学生信息存储到数组中并更新计数器 count,以记录当前学生数量。
关键点:
确保输入的数据类型正确,尤其是学号、年龄和成绩部分。
总成绩和平均成绩的计算要准确,并存储在结构体的相应字段中。
2. 删除学生成绩
算法设计思路:
接收用户输入的学生学号。
遍历学生信息数组,查找匹配的学号。
找到匹配项后,将该学生后的所有学生信息向前移动一个位置,从而覆盖被删除的学生信息。
更新学生数量计数器 count。
关键点:
正确处理数组元素的移动,确保删除操作后数组中的学生信息仍然连续且正确。
未找到匹配学号时给出相应提示。
3. 修改学生成绩
算法设计思路:
接收用户输入的学生学号。
遍历学生信息数组,查找匹配的学号。
找到匹配项后,接收新的各科成绩输入,并重新计算总成绩和平均成绩。
更新相应学生的成绩信息。
关键点:
确保输入的新成绩在合理范围内。
4. 查询学生成绩
算法设计思路:
提供两种查询方式:按学号查询和按姓名查询。
根据用户选择的查询方式,接收相应的查询条件(学号或姓名)。
遍历学生信息数组,查找匹配的学号或姓名。
找到匹配项后,输出该学生的所有信息,包括学号、姓名、年龄、各科成绩、总成绩和平均成绩。
关键点:
确保查询方式的选择和输入条件的获取准确无误。
输出信息时,格式化输出以保证信息清晰可读。
5. 按总成绩排名
算法设计思路:
实现一个简单的排序算法,如冒泡排序。
对学生信息数组按总成绩进行降序排列。
遍历数组两两比较相邻元素,如果前一个学生的总成绩小于后一个学生,则交换两个学生的位置。
排序完成后,输出所有学生的信息,格式化输出以便查看排名情况。
关键点:
排序算法的实现要正确,能够处理边界情况和异常情况。
排序后的输出要清晰显示每个学生的详细信息。
6. 数据保存到students.txt文件中
算法设计思路:
引入了 SaveToFile 和 LoadFromFile 函数,用于将学生信息存储到文件
从文件中加载,实现了数据持久化功能。
在 OverSystem 函数中增加了调用 SaveToFile 函数,确保系统退出前将数据保存到文件中。
7. 系统退出
算法设计思路:
提供安全退出选项,确认用户的退出操作。
清理必要的资源(如有),确保数据的完整性和安全性。
输出退出系统的提示信息。
四、调试分析过程描述:
测试数据
表一 测试数据表
测试分析结果
增添学生成绩:
成功添加上述三位学生的信息。
总成绩和平均成绩计算正确,存储正确。
删除学生成绩:
成功删除学生学号为1002的学生李四。
数组中剩余学生信息正确,未出现遗漏或多余信息。
修改学生成绩:
成功修改学生学号为1001的学生张三的成绩。
新成绩输入正确,总成绩和平均成绩重新计算正确。
查询学生成绩:
能够根据学号和姓名正确查询学生信息。
输出信息格式正确,数据完整。
按总成绩排名:
成功对剩余的两名学生按总成绩进行降序排列。
排序后的输出结果正确。
系统退出:
成功退出系统,显示退出成功信息。
过程中存在问题的思考
- 编码过程中出现在选择菜单输入错误重新输入而再输入正确选项但继续报错情况,通过查阅资料方式发现原因,具体原因:在函数Judge中,当用户输入有误时,程序应该能够重新提示用户输入,直到输入正确。存在问题,导致无法正确重新输入。为了确保用户能够重新输入直到正确,对Judge函数进行一些调整。
- 系统退出功能不能正常使用,查阅资料解决,具体原因:在main函数中的while循环永远不会停止,即使选择了退出系统的选项。在选择系统退出功能时跳出循环以终止程序。可以通过将一个退出标志引入到主循环来实现这一点。
五、测试结果:
所有功能的运行界面:
图二 增添学生成绩
图三 删除学生成绩
图四 修改学生成绩
图五 按学号查询学生成绩
图六 按姓名查询学生成绩
图七 按总成绩排名
结果分析:
增添学生成绩
操作:依次输入上述学生信息。
结果:系统成功添加三名学生的信息,并计算每个学生的总成绩和平均成绩。
验证:输入学生数据后,查询显示数据无误!
删除学生成绩
操作:输入学号1002删除学生李四的信息。
结果:李四的信息被成功删除,数据库中只剩下张三和王五的信息。
验证:查询结果显示剩余学生数据无误!
修改学生成绩
操作:修改学号1001的学生张三的高等数学成绩为88,大学英语成绩为90,程序设计成绩为90。
结果:张三的成绩被成功修改,总成绩和平均成绩也重新计算。
验证:查询结果显示更新后的信息正确!
查询学生成绩
操作:分别按学号和姓名查询学生信息。
结果:能够根据输入的学号或姓名准确查询到对应学生的详细信息。
验证:按学号查询1001无误!按姓名查询王五无误!
按总成绩排名
操作:按总成绩对学生进行降序排序。
结果:学生张三和王五按总成绩排序后,张三在前,王五在后。
验证:排序后的输出结果无误!
系统退出
操作:选择退出系统。
结果:系统成功退出,并显示退出成功的信息。
验证:退出系统时,显示“系统退出成功!”。
六、源代码:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <stdbool.h> #include <unistd.h> // 用于模拟延迟 // 定义学生结构体 struct student { long id; // 学号 char name[10]; // 姓名 int age; // 年龄 float AdvancedMath; // 高等数学成绩 float CollegeEnglish; // 大学英语成绩 float Programming; // 程序设计成绩 float sum; // 总成绩 float average; // 平均成绩 }; // 全局变量定义 struct student s[100]; // 学生信息数组 int count = 0; // 统计学生数量 // 函数声明 void Menu(); // 菜单显示函数 void AddStudent(); // 添加学生信息函数 void DeleteStudent(); // 删除学生信息函数 void ModifyStudent(); // 修改学生信息函数 void QueryStudent(); // 查询学生信息函数 void OutputStudents(); // 输出学生信息函数 void SortByTotalScore(); // 按总成绩排序函数 int Judge(int a, int b); // 输入判断函数 void OverSystem(); // 退出系统函数 void SaveToFile(); // 保存学生信息到文件函数 void LoadFromFile(); // 从文件加载学生信息函数 void Delay(int milliseconds); // 模拟延迟函数 // 主函数 int main() { LoadFromFile(); // 加载学生信息文件 int choice; // 存储用户选择 int exitFlag = 0; // 退出标志 while (!exitFlag) // 循环直到用户选择退出 { Menu(); // 显示菜单 printf("请选择您要使用的功能:"); choice = Judge(1, 7); // 获取用户选择 switch (choice) { case 1: // 添加学生信息 AddStudent(); SaveToFile(); // 每次操作后保存到文件 break; case 2: // 删除学生信息 DeleteStudent(); SaveToFile(); break; case 3: // 修改学生信息 ModifyStudent(); SaveToFile(); break; case 4: // 查询学生信息 QueryStudent(); break; case 5: // 按总成绩排序并输出学生信息 SortByTotalScore(); OutputStudents(); break; case 6: // 退出系统 OverSystem(); exitFlag = 1; break; case 7: // 显示系统使用说明 printf("\n欢迎使用学生成绩管理系统,该系统支持以下功能:\n"); printf("1. 增添学生成绩\n"); printf("2. 删除学生成绩\n"); printf("3. 修改学生成绩\n"); printf("4. 查询学生成绩\n"); printf("5. 按总成绩排名\n"); printf("6. 退出管理系统\n"); printf("7. 显示系统说明\n"); break; } Delay(500); // 模拟操作中的延迟 } return 0; // 主函数返回 } // 菜单显示函数 void Menu() { printf("\n*******学生成绩管理系统*******\n\n"); printf("1. 增添学生成绩\n"); printf("2. 删除学生成绩\n"); printf("3. 修改学生成绩\n"); printf("4. 查询学生成绩\n"); printf("5. 按总成绩排名\n"); printf("6. 退出管理系统\n"); printf("7. 显示系统使用说明\n"); } // 输入判断函数 int Judge(int a, int b) { int choice; // 存储用户输入 while (1) { if (scanf("%d", &choice) != 1 || choice < a || choice > b) { while (getchar() != '\n'); // 清空输入缓冲区 printf("输入有误,请重新输入:"); } else { break; // 输入有效,跳出循环 } } return choice; // 返回用户选择 } // 添加学生信息函数 void AddStudent() { printf("请输入学生学号:"); scanf("%ld", &s[count].id); // 输入学号 printf("请输入学生姓名:"); scanf("%s", s[count].name); // 输入姓名 printf("请输入学生年龄:"); s[count].age = Judge(1, 150); // 输入年龄 printf("请输入高等数学成绩:"); s[count].AdvancedMath = Judge(0, 100); // 输入高等数学成绩 printf("请输入大学英语成绩:"); s[count].CollegeEnglish = Judge(0, 100); // 输入大学英语成绩 printf("请输入程序设计成绩:"); s[count].Programming = Judge(0, 100); // 输入程序设计成绩 s[count].sum = s[count].AdvancedMath + s[count].CollegeEnglish + s[count].Programming; // 计算总成绩 s[count].average = s[count].sum / 3; // 计算平均成绩 count++; // 学生数量加1 printf("添加成功!\n"); // 输出添加成功信息 } // 删除学生信息函数 void DeleteStudent() { long id; // 存储要删除的学生学号 printf("请输入要删除的学生学号:"); scanf("%ld", &id); // 输入学号 int i; for (i = 0; i < count; i++) // 查找学号 { if (s[i].id == id) // 找到学号 { int j; for (j = i; j < count - 1; j++) // 删除学生信息,数组前移 { s[j] = s[j + 1]; } count--; // 学生数量减1 printf("删除成功!\n"); // 输出删除成功信息 return; // 退出函数 } } printf("未找到学生信息!\n"); // 输出未找到学生信息 } // 修改学生信息函数 void ModifyStudent() { long id; // 存储要修改的学生学号 printf("请输入要修改的学生学号:"); scanf("%ld", &id); // 输入学号 int i; for (i = 0; i < count; i++) // 查找学号 { if (s[i].id == id) // 找到学号 { printf("请输入新的高等数学成绩:"); s[i].AdvancedMath = Judge(0, 100); // 输入新的高等数学成绩 printf("请输入新的大学英语成绩:"); s[i].CollegeEnglish = Judge(0, 100); // 输入新的大学英语成绩 printf("请输入新的程序设计成绩:"); s[i].Programming = Judge(0, 100); // 输入新的程序设计成绩 s[i].sum = s[i].AdvancedMath + s[i].CollegeEnglish + s[i].Programming; // 重新计算总成绩 s[i].average = s[i].sum / 3; // 重新计算平均成绩 printf("修改成功!\n"); // 输出修改成功信息 return; // 退出函数 } } printf("未找到学生信息!\n"); // 输出未找到学生信息 } // 查询学生信息函数 void QueryStudent() { int choice; // 存储查询方式 printf("请选择查询方式:1. 按学号查询 2. 按姓名查询\n"); choice = Judge(1, 2); // 获取用户选择 switch (choice) { case 1: // 按学号查询 { long id; // 存储要查询的学生学号 printf("请输入要查询的学生学号:"); scanf("%ld", &id); // 输入学号 int i; for (i = 0; i < count; i++) // 查找学号 { if (s[i].id == id) // 找到学号 { printf("学号:%ld 姓名:%s 年龄:%d 高等数学:%.2f 大学英语:%.2f 程序设计:%.2f 总分:%.2f 平均分:%.2f\n", s[i].id, s[i].name, s[i].age, s[i].AdvancedMath, s[i].CollegeEnglish, s[i].Programming, s[i].sum, s[i].average); // 输出学生信息 return; // 退出函数 } } printf("未找到学生信息!\n"); // 输出未找到学生信息 break; } case 2: // 按姓名查询 { char name[10]; // 存储要查询的学生姓名 printf("请输入要查询的学生姓名:"); scanf("%s", name); // 输入姓名 int i; for (i = 0; i < count; i++) // 查找姓名 { if (strcmp(s[i].name, name) == 0) // 如果找到与输入姓名匹配的学生信息 { printf("学号:%ld 姓名:%s 年龄:%d 高等数学:%.2f 大学英语:%.2f 程序设计:%.2f 总分:%.2f 平均分:%.2f\n", s[i].id, s[i].name, s[i].age, s[i].AdvancedMath, s[i].CollegeEnglish, s[i].Programming, s[i].sum, s[i].average); // 输出学生信息 return; // 找到学生信息,退出函数 } } printf("未找到学生信息!\n"); // 循环结束仍未找到匹配的姓名,输出未找到学生信息 break; // 结束switch语句中的case 2 } } } // 输出学生信息函数 void OutputStudents() { float totalAdvancedMath = 0; // 初始化总成绩变量 float totalCollegeEnglish = 0; float totalProgramming = 0; float totalSum = 0; printf("学号\t姓名\t年龄\t高等数学\t大学英语\t程序设计\t总分\t平均分\n"); // 输出表头 int i; for (i = 0; i < count; i++) // 循环输出每个学生的详细信息 { printf("%ld\t%s\t%d\t%.2f\t\t%.2f\t\t%.2f\t\t%.2f\t%.2f\n", s[i].id, s[i].name, s[i].age, s[i].AdvancedMath, s[i].CollegeEnglish, s[i].Programming, s[i].sum, s[i].average); totalAdvancedMath += s[i].AdvancedMath; // 计算总成绩 totalCollegeEnglish += s[i].CollegeEnglish; totalProgramming += s[i].Programming; totalSum += s[i].sum; } printf("班级平均\t\t%.2f\t\t%.2f\t\t%.2f\t\t%.2f\t%.2f\n", // 输出班级平均成绩 totalAdvancedMath / count, totalCollegeEnglish / count, totalProgramming / count, totalSum / count, totalSum / count / 3); } // 按总成绩排序函数 void SortByTotalScore() { int i, j; for (i = 0; i < count - 1; i++) // 冒泡排序,按总成绩降序排列学生信息 { for (j = i + 1; j < count; j++) { if (s[i].sum < s[j].sum) { struct student temp; // 交换学生信息 temp = s[i]; s[i] = s[j]; s[j] = temp; } } } } // 退出系统函数 void OverSystem() { SaveToFile(); // 系统退出前保存到文件 printf("系统退出成功!\n"); // 输出退出成功信息 } // 保存学生信息到文件函数 void SaveToFile() { FILE *fp; fp = fopen("students.txt", "w"); // 打开文件准备写入 if (fp == NULL) // 检查文件是否成功打开 { printf("无法打开文件!\n"); // 输出无法打开文件信息 return; } int i; for (i = 0; i < count; i++) // 遍历学生信息数组,将信息写入文件 { fprintf(fp, "%ld %s %d %.2f %.2f %.2f %.2f %.2f\n", s[i].id, s[i].name, s[i].age, s[i].AdvancedMath, s[i].CollegeEnglish, s[i].Programming, s[i].sum, s[i].average); } fclose(fp); // 关闭文件 } // 从文件加载学生信息函数 void LoadFromFile() { FILE *fp; fp = fopen("students.txt", "r"); // 打开文件准备读取 if (fp == NULL) // 检查文件是否成功打开 { printf("未找到存储文件,将从头开始录入学生信息。\n"); // 输出未找到文件信息 return; } while (fscanf(fp, "%ld %s %d %f %f %f %f %f\n", // 从文件中读取学生信息 &s[count].id, s[count].name, &s[count].age, &s[count].AdvancedMath, &s[count].CollegeEnglish, &s[count].Programming, &s[count].sum, &s[count].average) != EOF) { count++; // 统计加载的学生数量 } fclose(fp); // 关闭文件 } // 模拟延迟函数 void Delay(int milliseconds) { usleep(milliseconds * 1000); // 将毫秒转换为微秒,模拟延迟 }