本次实验目的:训练逆向软件设计与开发能力。
实验内容:找一个已有的项目,阅读分析,找出软件尚存的缺陷,改进其软件做二次开发,并将过程整理成博客。
来源:CSDN上的学生管理系统
链接:
https://blog.csdn.net/weixin_74362817/article/details/142308755fromshare=blogdetail&sharetype=blogdetail&sharerId=142308755&sharerefer=PC&sharesource=&sharefrom=from_link
代码:
点击查看代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>// 定义常量
#define MAX_STUDENTS 100 // 最大学生数量
#define NAME_LENGTH 50 // 姓名最大长度
#define MAJOR_LENGTH 30 // 专业名称最大长度// 定义学生结构体
typedef struct {int id; // 学号char name[NAME_LENGTH]; // 姓名char gender; // 性别int age; // 年龄char major[MAJOR_LENGTH]; // 专业
} Student;// 全局变量
Student students[MAX_STUDENTS]; // 学生数组
int student_count = 0; // 当前学生计数// 函数声明
void add_student(); // 添加学生
void delete_student(); // 删除学生
void modify_student(); // 修改学生信息
void query_student(); // 查询学生信息
void load_data(); // 加载学生数据
void save_data(); // 保存学生数据
void display_students();// 显示所有学生信息int main() {load_data(); // 加载已有的学生数据int choice;// 主菜单循环do {printf("\n学生管理系统\n");printf("1. 添加学生\n");printf("2. 删除学生\n");printf("3. 修改学生\n");printf("4. 查询学生\n");printf("5. 保存数据\n");printf("6. 退出\n");printf("请输入您的选择: ");scanf("%d", &choice);// 根据用户选择执行相应操作switch (choice) {case 1: add_student(); break;case 2: delete_student(); break;case 3: modify_student(); break;case 4: query_student(); break;case 5: save_data(); break;case 6: printf("退出系统。\n"); break;default: printf("无效选择,请重试。\n");}} while (choice != 6); // 当选择6时退出return 0;
}// 添加学生
void add_student() {// 检查是否达到最大学生数量if (student_count >= MAX_STUDENTS) {printf("学生数量已满,无法添加更多学生。\n");return;}// 创建新的学生结构体并输入信息Student new_student;printf("请输入学号: ");scanf("%d", &new_student.id);printf("请输入姓名: ");scanf("%s", new_student.name);printf("请输入性别 (M/F): ");scanf(" %c", &new_student.gender);printf("请输入年龄: ");scanf("%d", &new_student.age);printf("请输入专业: ");scanf("%s", new_student.major);// 将新学生添加到数组并增加计数students[student_count++] = new_student;printf("学生信息已添加。\n");
}// 删除学生
void delete_student() {int id;printf("请输入要删除的学号: ");scanf("%d", &id);// 查找并删除匹配学号的学生for (int i = 0; i < student_count; i++) {if (students[i].id == id) {// 移动后续学生位置for (int j = i; j < student_count - 1; j++) {students[j] = students[j + 1];}student_count--; // 减少学生计数printf("学生信息已删除。\n");return;}}printf("未找到学号为 %d 的学生。\n", id);
}// 修改学生
void modify_student() {int id;printf("请输入要修改的学号: ");scanf("%d", &id);// 查找并修改匹配学号的学生信息for (int i = 0; i < student_count; i++) {if (students[i].id == id) {printf("当前信息: 姓名: %s, 性别: %c, 年龄: %d, 专业: %s\n",students[i].name, students[i].gender, students[i].age, students[i].major);printf("请输入新姓名: ");scanf("%s", students[i].name);printf("请输入新性别 (M/F): ");scanf(" %c", &students[i].gender);printf("请输入新年龄: ");scanf("%d", &students[i].age);printf("请输入新专业: ");scanf("%s", students[i].major);printf("学生信息已修改。\n");return;}}printf("未找到学号为 %d 的学生。\n", id);
}// 查询学生
void query_student() {int id;printf("请输入要查询的学号 (0 列出所有学生): ");scanf("%d", &id);// 如果输入0则显示所有学生if (id == 0) {display_students();} else {// 查找并显示匹配学号的学生信息for (int i = 0; i < student_count; i++) {if (students[i].id == id) {printf("学号: %d, 姓名: %s, 性别: %c, 年龄: %d, 专业: %s\n",students[i].id, students[i].name, students[i].gender, students[i].age, students[i].major);return;}}printf("未找到学号为 %d 的学生。\n", id);}
}// 显示所有学生
void display_students() {// 检查是否有学生信息if (student_count == 0) {printf("无学生信息。\n");return;}// 逐个打印学生信息printf("学生信息列表:\n");for (int i = 0; i < student_count; i++) {printf("学号: %d, 姓名: %s, 性别: %c, 年龄: %d, 专业: %s\n",students[i].id, students[i].name, students[i].gender, students[i].age, students[i].major);}
}// 加载数据
void load_data() {// 打开文件读取学生信息FILE *file = fopen("students.txt", "r");if (file == NULL) {printf("未找到学生数据文件,使用空数据。\n");return;}// 读取文件内容并存储到学生数组while (fscanf(file, "%d %s %c %d %s", &students[student_count].id,students[student_count].name, &students[student_count].gender,&students[student_count].age, students[student_count].major) != EOF) {student_count++;}fclose(file); // 关闭文件
}// 保存数据
void save_data() {// 打开文件保存学生信息FILE *file = fopen("students.txt", "w");if (file == NULL) {printf("无法打开文件保存数据。\n");return;}// 将学生信息写入文件for (int i = 0; i < student_count; i++) {fprintf(file, "%d %s %c %d %s\n", students[i].id, students[i].name,students[i].gender, students[i].age, students[i].major);}fclose(file); // 关闭文件printf("学生信息已保存。\n");
}
一、在原有系统上进行分析
数据结构:代码中定义了一个Student结构体,用于存储学生的基本信息(学号、姓名、性别、年龄、专业)。
全局变量:students数组用于存储所有学生信息,student_count用于记录当前学生数量。
功能模块:add_student:添加学生信息。
delete_student:删除学生信息。
modify_student:修改学生信息。
query_student:查询学生信息。
load_data:从文件加载学生数据。
save_data:将学生数据保存到文件。
display_students:显示所有学生信息。
二、系统的优缺点
优点:
- 功能完整,满足基本需求
系统实现了学生信息管理的核心功能,包括添加、删除、修改、查询和显示学生信息,能够满足基本的使用场景。
提供了数据的持久化功能(通过文件存储),确保数据在程序退出后不会丢失。 - 代码结构清晰
系统采用模块化设计,将不同功能封装为独立的函数,便于理解和维护。
代码逻辑简单直接,适合初学者学习和理解。
缺点问题:
1、缺乏数据验证
输入时没有对用户输入进行验证(如学号是否重复、性别是否合法等)。
2、代码存在的问题
代码中有此display_students(显示所有学生信息)函数,功能中却没有展示出来。
三、进行二次开发
1、在输入学生信息时增加验证逻辑,确保数据的合法性。
新代码:
点击查看代码
void add_student() {// 检查是否达到最大学生数量if (student_count >= MAX_STUDENTS) {printf("学生数量已满,无法添加更多学生。\n");return;}// 创建新的学生结构体并输入信息Student new_student;printf("请输入学号: ");while (scanf("%d", &new_student.id) != 1) {printf("无效输入,请输入一个整数: ");while (getchar() != '\n'); // 清除缓冲区}printf("请输入姓名: ");scanf("%s", new_student.name);printf("请输入性别 (M/F): ");while (scanf(" %c", &new_student.gender) != 1 || (new_student.gender != 'M' && new_student.gender != 'F')) {printf("无效输入,请输入 M 或 F: ");while (getchar() != '\n'); // 清除缓冲区}printf("请输入年龄: ");while (scanf("%d", &new_student.age) != 1 || new_student.age < 0) {printf("无效输入,请输入一个正整数: ");while (getchar() != '\n'); // 清除缓冲区}printf("请输入专业: ");scanf("%s", new_student.major);// 将新学生添加到数组并增加计数students[student_count++] = new_student;printf("学生信息已添加。\n");
}
2、在功能中补上显示所有学生信息功能。
新代码:
点击查看代码
do {printf("\n学生管理系统\n");printf("1. 添加学生\n");printf("2. 删除学生\n");printf("3. 修改学生\n");printf("4. 查询学生\n");printf("5. 保存数据\n");printf("6. 显示所有学生信息");printf("7. 退出\n");printf("请输入您的选择: ");scanf("%d", &choice);
重构软件测试截图:
四、总结
难点:
代码逻辑理解:原代码中包含多个功能函数,每个函数又有其特定的逻辑,如删除学生信息时需要将后续学生前移,这涉及到数组元素的移动操作,理解起来有一定难度。特别是对于没有相关编程经验的人来说,要理清每个函数的具体实现和调用关系需要花费较多时间。主函数中的菜单循环和 switch 语句的结合,通过用户输入的选择来调用不同的功能函数,逻辑较为复杂,需要仔细分析才能掌握整个程序的流程。
输入验证的实现:为了增强系统的健壮性,添加输入验证功能是必要的。但实现输入验证需要考虑各种可能的输入情况,如输入非整数、输入不符合要求的性别等。在处理输入验证时,需要使用循环和条件判断来确保用户输入的是合法数据,这增加了代码的复杂度。
花费时间较久的部分:输入验证功能的实现,实现输入验证时,需要考虑不同类型的输入错误,并进行相应的处理。例如,在输入学号、年龄等整数时,要确保用户输入的是有效的整数;在输入性别时,要确保用户输入的是 M 或 F。为了处理这些错误输入,需要使用循环和条件判断来不断提示用户重新输入,直到输入合法数据为止。这个过程需要进行多次测试和调试,以确保输入验证的准确性和可靠性。
思考:我知道了在逆向软件工程中代码分析的重要性,逆向软件工程的第一步是对已有代码进行理解和分析。只有深入理解代码的逻辑和结构,才能准确地把握系统的功能和实现细节,为后续的改进和扩展打下基础。在这个学生管理系统的逆向开发过程中,通过仔细阅读代码,我们能够理解每个功能函数的作用和相互之间的调用关系,从而更好地进行改进。在面对缺乏文档的旧系统时,逆向分析可揭示其架构、功能逻辑,为维护和升级提供依据。像对学生管理系统的逆向,能让开发者把握数据处理和功能实现细节,进行针对性优化。同时,逆向工程有助于代码复用,避免重复开发,节省时间和资源。