对同学大一第一学期C语言期末课程设计的修改
该项目是一位同学大一第一学期的时候写的一个C语言期末课程设计,经过了一年的学习,不论是对程序设计的理解还是经验都有所提升,现在回过去看以前的代码,存在不少值得关注和修改的地方。
原题如下:
1.要求实现学生信息的查找、添加、删除、修改、浏览、保存、从文件读取、查看奖学金信息8个功能,每个功能模块均能实现随时从模块中退出,而且可以选择不同的方式实现所需功能,从而完成一个学生管理系统所需功能。
2.要使用结构体来实现对学生信息的存储。
3.可使用链表或数组来实现对学生信息的查找、添加、删除、修改、浏览等操作。
4.使用文件完成数据的存储与读取,要求每次运行某个功能模块时将数据读入结构体中,并给用户提供保存选项,可以将结构体中的数据保存在文件中。
其中,该同学对结构体的定义如下:
点击查看代码
typedef struct Student {
int id;
char name[50];
float grade;
char scholarship[50];
struct Student *next;
} Student;
本次优化暂不考虑避免输入类型错误,在开始测试原始程序前,首先对所涉及的变量类型进行定义:
| 变量 | 类型 |
|---|---|
| id | 3位整数,且不以0打头 |
| name[50] | 无空格中文字符 |
| grade | 0-100的整数 |
| name[50] | “一等奖”、“二等奖”、“三等奖”或“无” |
| 以上变量类型定义仅为统一测试流程,提升测试效率。 |
为节约篇幅,测试程序过程中,主菜单仅保留第一次出现的完整界面,其余出现仅保留一行*号以示分割。
首先查看添加学生功能的代码:(删去了分配内存空间失败时的抛出代码)
点击查看代码
void addStudent() {
Student *new_student = (Student *)malloc(sizeof(Student));
printf("输入学生ID: ");
scanf("%d", &new_student->id);
printf("输入学生姓名: ");
scanf("%s", new_student->name);
printf("输入学生成绩: ");
scanf("%f", &new_student->grade);
printf("输入奖学金信息: ");
scanf("%s", new_student->scholarship);
new_student->next = NULL;
if (head == NULL) {
head = new_student;
} else {
Student *temp = head;
while (temp->next != NULL) {
temp = temp->next;
}
temp->next = new_student;
}
}
此段代码在输入时不对学生ID进行检查,因此可能存在一个ID对应多个学生的情况,例如:
点击查看代码
*****************************
学生信息管理系统
1. 添加学生
2. 删除学生
3. 修改学生信息
4. 浏览学生信息
5. 保存学生信息
6. 从文件读取学生信息
7. 查看奖学金信息
8. 批量添加学生信息
0. 退出
*****************************
请选择操作:1
输入学生ID: 101
输入学生姓名: 无声铃鹿
输入学生成绩: 90
输入奖学金信息: 一等奖
*****************************
请选择操作:4
ID: 101, 姓名: 无声铃鹿, 成绩: 90.00, 奖学金: 一等奖
*****************************
请选择操作:5
学生信息已保存
*****************************
请选择操作:6
学生信息已加载
*****************************
请选择操作:4
ID: 101, 姓名: 无声铃鹿, 成绩: 90.00, 奖学金: 一等奖
ID: 101, 姓名: 无声铃鹿, 成绩: 90.00, 奖学金: 一等奖
因此需要增加代码,在输入重复时抛出异常。为使代码更加人性化,在输入ID后立刻进行查重。在scanf("%d", &new_student->id);后增加以下代码块:
点击查看代码
Student *temp = head;
int isDuplicate = 0;
while (temp != NULL) {
if (temp->id == new_student->id) {
isDuplicate = 1;
break;
}
temp = temp->next;
}
if (isDuplicate) {
printf("错误:ID已存在\n");
return;
}
其中变量isDuplicate用于指示是否发生重复。添加代码后,在输入重复的ID时,将会提示“错误:ID已存在”,而不会记录重复的ID,如下所示:
点击查看代码
请选择操作:1
输入学生ID: 101
错误:ID已存在
接下来查看读取文件代码:
点击查看代码
void loadStudents() {
FILE *file = fopen("students.dat", "rb");
if (file == NULL) {
printf("文件打开失败\n");
return;
}
Student *temp;
while (1) {
temp = (Student *)malloc(sizeof(Student));
if (fread(temp, sizeof(Student), 1, file) == 0) {
free(temp);
break;
}
temp->next = NULL;
appendToTail(temp);
}
fclose(file);
printf("学生信息已加载\n");
}
在当前代码中,读取文件会追加在内存已有的数据之后,可能导致重复,如下操作:
点击查看代码
*****************************
学生信息管理系统
1. 添加学生
2. 删除学生
3. 修改学生信息
4. 浏览学生信息
5. 保存学生信息
6. 从文件读取学生信息
7. 查看奖学金信息
8. 批量添加学生信息
0. 退出
*****************************
请选择操作:1
输入学生ID: 101
输入学生姓名: 无声铃鹿
输入学生成绩: 90
输入奖学金信息: 一等奖
*****************************
请选择操作:4
ID: 101, 姓名: 无声铃鹿, 成绩: 90.00, 奖学金: 一等奖
*****************************
请选择操作:5
学生信息已保存
*****************************
请选择操作:6
学生信息已加载
*****************************
请选择操作:4
ID: 101, 姓名: 无声铃鹿, 成绩: 90.00, 奖学金: 一等奖
ID: 101, 姓名: 无声铃鹿, 成绩: 90.00, 奖学金: 一等奖
因此增加函数freeStudents()释放内存中的数据:
点击查看代码
void freeStudents() {
Student *temp = head;
while (temp != NULL) {
Student *next = temp->next;
free(temp);
temp = next;
}
head = NULL;
}
同时在if(file == NULL){}后,增加一行freeStudents()以清理内存。
再次进行测试,测试结果如下:
点击查看代码
*****************************
学生信息管理系统
1. 添加学生
2. 删除学生
3. 修改学生信息
4. 浏览学生信息
5. 保存学生信息
6. 从文件读取学生信息
7. 查看奖学金信息
8. 批量添加学生信息
0. 退出
*****************************
请选择操作:1
输入学生ID: 101
输入学生姓名: 无声铃鹿
输入学生成绩: 90
输入奖学金信息: 一等奖
*****************************
请选择操作:4
ID: 101, 姓名: 无声铃鹿, 成绩: 90.00, 奖学金: 一等奖
*****************************
请选择操作:5
学生信息已保存
*****************************
请选择操作:6
学生信息已加载
*****************************
请选择操作:4
ID: 101, 姓名: 无声铃鹿, 成绩: 90.00, 奖学金: 一等奖
在逆向软件重新开发的过程中,遇到了不少难点。
第一是不同人的代码缩进的风格不同。我同学的代码风格以
if(){
……
}
为主,而我个人习惯于使用
if()
{
……
}
进行代码块标记。(“{”位置不同)
第二是变量及函数命名的风格不同。我个人习惯在不同单词中以“_”进行连接,而不是仅仅依赖于字母的大小写进行单词的划分。例如代码中的appendToTail()函数,我倾向于写作append_To_Tail()。
以上两点都会影响代码的可读性,并且不同风格若交替使用,将会极大降低后续的可维护性。因此在代码二次开发的过程中,选择了维持原代码的缩进和命名风格。
其次,尽管原代码带有较为详尽的注释,但在链表的操作习惯有较大不同,需要额外进行分析原作者对链表的操作方式。尤其是在使用了自己所不熟悉的链表插入方式时。对链表操作的分析一定程度上这也降低了程序维护的效率。

浙公网安备 33010602011771号