大家好,欢迎来到我的博客总结应用。在上一篇博客中,我写了有关结构体和内存操作函
数的总结,这些博客记录了我的学习、思考和经验。为了更好地总结和回顾这些内容,在此
篇博客中,我编写了”学生管理系统“来帮助我整理和应用上一篇博客的总结知识点。在这个
”学生管理系统“中,我列举了九条不同的标题和内容来进行说明,以下便是我的总结整理。
一、定义学生信息的结构体
添加必要的头文件和声明,之后定义学生信息结构体。
学生信息结构体中含有必要的学生id、学生姓名和学生成绩,除此之外,也可以自行添加一些其他信息,如学生家庭年龄,学生住址,学生班级,学生教师,学生所属学校等
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 学生信息结构体
typedef struct _Student {
int id;
float score; /* 数据域 */
char name[50];
struct Student *next; /* 指针域 */
} Student;
int isExistId(Student **head, int id);
void freeMemory(Student *head);
void addStudent(Student **head);
void deleteStudent(Student **head);
void updateStudent(Student **head);
void printStudent(Student **head);
void searchStudent(Student **head);
可以将数据域和指针域分开来定义,使结构逻辑更加严整。
typedef struct _Student {
int id;
float score;
char name[50];
} Student;
// 嵌套结构体
typedef struct _StuNode {
Student stu;
struct _Node *next;
} StuNode;
二、主函数逻辑
主函数主要负责输出学生管理系统的文字页面、选择对应的功能、暂停并清空控制台
int main()
{
int choice;
Student *head = NULL; // 学生链表头指针
while (1)
{
// 暂停程序
system("pause");
// 清空控制台
system("cls");
printf("--------学生管理系统--------\n");
printf("\t1. 添加学生信息\n");
printf("\t2. 删除学生信息\n");
printf("\t3. 修改学生信息\n");
printf("\t4. 显示学生列表\n");
printf("\t5. 查找学生信息\n");
printf("\t0. 退出系统\n");
printf("请选择操作:");
// 输入非数字字符,清空输入缓冲区
if (scanf("%d", &choice) != 1)
{
while (getchar() != '\n');
printf("无效的操作,请重新选择。\n");
continue;
}
// 当输入数字字符后,防止缓冲区中的字符影响自己的选择
while (getchar() != '\n');
switch (choice)
{
case 1:
addStudent(&head); // 添加学生信息
break;
case 2:
deleteStudent(&head); // 删除学生信息
break;
case 3:
updateStudent(&head); // 更新学生信息
break;
case 4:
printStudent(&head); // 打印学生信息
break;
case 5:
searchStudent(&head); // 搜索学生信息
break;
case 0:
printf("感谢使用学生管理系统,再见!\n");
freeMemory(head); // 释放内存,防止内存泄漏
exit(0);
default:
printf("无效操作,请重新选择。\n");
}
}
return 0;
}
注意,学生管理系统是需要手动退出的一个页面,所以很多操作都是在一个循环里面。
-
输出学生管理系统的文字页面
按照一定格式输出文字,将其美观排版后输出即可
// 文字页面 printf("--------学生管理系统--------\n"); printf("\t1. 添加学生信息\n"); printf("\t2. 删除学生信息\n"); printf("\t3. 修改学生信息\n"); printf("\t4. 显示学生列表\n"); printf("\t5. 查找学生信息\n"); printf("\t0. 退出系统\n"); printf("请选择操作:");
-
选择对应的功能
使用switch语句,每一种case对应一种功能,此外还需要一定的防御性编程,如选择时要求输入数字字符,消除输入数字字符的后面其他无关字符的影响
// 输入非数字字符,清空输入缓冲区 if (scanf("%d", &choice) != 1) { while (getchar() != '\n'); printf("无效的操作,请重新选择。\n"); continue; } // 当输入数字字符后,防止缓冲区中的字符影响自己的选择 while (getchar() != '\n'); switch (choice) { case 1: addStudent(&head); // 添加学生信息 break; case 2: deleteStudent(&head); // 删除学生信息 break; case 3: updateStudent(&head); // 更新学生信息 break; case 4: printStudent(&head); // 打印学生信息 break; case 5: searchStudent(&head); // 搜索学生信息 break; case 0: printf("感谢使用学生管理系统,再见!\n"); freeMemory(head); // 释放内存,防止内存泄漏 exit(0); default: printf("无效操作,请重新选择。\n"); }
-
暂停并清空控制台
为了让输出界面和输出的结果更加直观,所以要求暂停程序观看,之后清空控制台以达到简洁直观的目的
// 暂停程序 system("pause"); // 清空控制台 system("cls");
-
三、添加学生信息
-
录入学生信息
添加学生信息这一环节主要是创建一个结点,结点中含有填写完的信息,然后把这个结点放在链表之中。
// 添加学生信息 void addStudent(Student **head) { int id; printf("请输入学生id: "); scanf("%d", &id); // 判断是否重复录取学生信息 if (IsExistId(head, id)) { printf("已存在id为%d的学生信息!\n", id); return; } // 开辟空间,并填写信息 Student *newStudent = (Student *) malloc(sizeof(Student)); newStudent->next = NULL; char name[50]; float score; printf("请输入学生姓名: "); scanf("%s", name); printf("请输入学生分数: "); scanf("%f", &score); newStudent->id = id; /* 注意字符串赋值的方式 */ strcpy(newStudent->name, name); newStudent->score = score; // 尾插法:将结点插入在链表之后 if (*head == NULL) { *head = newStudent; } else { Student *temp = *head; while (temp->next != NULL) { temp = temp->next; } temp->next = newStudent; } }
-
插入结点的方法
插入节点的两个方法:头插法和尾插法
-
头插法:将结点放在头部,使之成为新的头结点
// 头插法 // 无论有没有头结点都可以 newStudent->next = *head; *head = newStudent;
-
尾插法:不断地将结点插在链表尾部
// 尾插法:将结点插入在链表之后 // 情况一,没有头结点 if (*head == NULL) { *head = newStudent; } // 情况二,有头结点 else { Student *temp = *head; // 一直找到最后一个结点 while (temp->next != NULL) { temp = temp->next; } temp->next = newStudent; }
-
-
避免重复录入学生信息
为了不重复录入学生信息,定义一个IsExistId函数,该函数的实现就是遍历链表,判断链表中是否存在待录入学生的id,如果存在,返回1;否则,返回0。
int IsExistId(Student *head, int id) { while (head != NULL) { if (head->id == id) { return 1; } } return 0; }
四、删除学生信息
-
删除学生信息
这意味着将去除链表中指定学生信息的结点,然后释放内存。
// 删除学生信息 void deleteStudent(Student **head) { int id; Student *temp = *head; // 待删除结点的前一个结点,用于指向待删除结点的下一个结点 Student *prev = NULL; printf("请输入要删除的学生id: "); scanf("%d", &id); // 遍历链表,查找要删除的学生结点位置 while (temp != NULL && temp->id != id) { prev = temp; temp = temp->next; } // 说明链表为空,或者到了链表尾部,即没有找到指定学生id if (temp == NULL) { printf("未找到学生id为%d的记录。\n", id); return; } // 如果找到了要删除的学生,从链表中删除它 else { if (prev == NULL) { // 删除头节点 *head = temp->next; } else { prev->next = temp->next; } free(temp); printf("已删除学生id为%d的记录。\n", id); } }
-
删除学生信息结点有两个位置区分
- 头结点位置:删除头结点后,头结点的下一位即为头结点
- 其他位置:待删除结点的前一位,它的下一位是待删除结点的下一位
if (prev == NULL) { // 删除头节点 *head = temp->next; } else { prev->next = temp->next; } free(temp);
五、修改学生信息
遍历链表,找到待修改信息的学生id即可修改学生信息
// 修改学生信息
void updateStudent(Student **head)
{
int id;
Student *temp = *head;
printf("请输入要修改信息的学生id: ");
scanf("%d", &id);
while (temp != NULL && temp->id != id)
{
temp = temp->next;
}
// 要么链表为空,要么已经到链表尾部,这两种情况都是未找到id
if (temp == NULL)
{
printf("未找到学生id为%d的记录。\n", id);
}
else
{
printf("请输入新的姓名和成绩:\n");
scanf("%s %f", temp->name, &temp->score);
printf("已更新学生id为%d的信息。\n", id);
}
}
六、显示学生列表
遍历链表,将学生信息一个个打印出来。这个过程是按照链表的插入顺序进行打印输出的,还可以自己实现按照id大小排序进行输出,抑或着按照学生成绩大小排序进行输出。
// 打印学生信息
void printStudent(Student **head)
{
if (*head == NULL)
{
printf("暂未录入学生信息\n");
return;
}
printf("\n学生信息如下:\n");
Student *temp = *head;
while (temp != NULL)
{
printf("ID:%d,姓名:%s,成绩:%.2f\n", temp->id, temp->name, temp->score);
temp = temp->next;
}
}
七、查询学生信息
本质上还是遍历链表,查看是否存在指定id的人
void searchStudent(Student **head) {
int id;
Student *temp = *head;
printf("输入待搜索学生的id: ");
scanf("%d", &id);
while(temp != NULL && temp->id != id) {
temp = temp->next;
}
if (temp == NULL) {
printf("查无此id:%d信息。", id);
}
else
{
printf("id: %d name: %s score: %.2f\n", id, temp->name, temp->score);
}
}
八、退出程序
退出程序只需要在switch中添加一个选项即可,在这个选项中有exit。当然,退出程序前,要主动释放内存,避免内存泄漏。
以下这个函数也i将添加在含有exit选项中。
// 释放内存空间
void freeMemory(Student *head)
{
Student *temp;
while (head != NULL)
{
temp = head;
head = head->next;
free(temp);
}
}
九、完整代码及补充
以上就是学生管理系统的大致代码,之后若是学习了文件管理相关的函数,还可以将文件导出保存,将其他的对应格式的学生信息文件导入进行管理。除了文件管理相关的函数,我们还可以将数据导入到数据库中,这些都是之后将会学习的知识。
-
完整代码
#include <stdio.h> #include <stdlib.h> #include <string.h> // 学生信息结构体 typedef struct Student { int id; float score; /* 数据域 */ char name[50]; struct Student *next; /* 指针域 */ } Student; int isExistId(Student **head, int id); void freeMemory(Student *head); void addStudent(Student **head); void deleteStudent(Student **head); void updateStudent(Student **head); void printStudent(Student **head); void searchStudent(Student **head); int main() { int choice; Student *head = NULL; // 学生链表头指针 while (1) { // 暂停程序 system("pause"); // 清空控制台 system("cls"); printf("--------学生管理系统--------\n"); printf("\t1. 添加学生信息\n"); printf("\t2. 删除学生信息\n"); printf("\t3. 修改学生信息\n"); printf("\t4. 显示学生列表\n"); printf("\t5. 查找学生信息\n"); printf("\t0. 退出系统\n"); printf("请选择操作:"); // 输入非数字字符,清空输入缓冲区 if (scanf("%d", &choice) != 1) { while (getchar() != '\n') ; printf("无效的操作,请重新选择。\n"); continue; } // 当输入数字字符后,防止缓冲区中的字符影响自己的选择 while (getchar() != '\n'); switch (choice) { case 1: addStudent(&head); // 添加学生信息 break; case 2: deleteStudent(&head); // 删除学生信息 break; case 3: updateStudent(&head); // 更新学生信息 break; case 4: printStudent(&head); // 打印学生信息 break; case 5: searchStudent(&head); // 搜索学生信息 break; case 0: printf("感谢使用学生管理系统,再见!\n"); freeMemory(head); // 释放内存,防止内存泄漏 exit(0); default: printf("无效操作,请重新选择。\n"); } } return 0; } // 添加学生信息 void addStudent(Student **head) { int id; printf("请输入学生id: "); scanf("%d", &id); // 判断是否重复录取学生信息 if (isExistId(head, id)) { printf("已存在id为%d的学生信息!\n", id); return; } // 开辟空间,并填写信息 Student *newStudent = (Student *)malloc(sizeof(Student)); newStudent->next = NULL; char name[50]; float score; printf("请输入学生姓名: "); scanf("%s", name); printf("请输入学生分数: "); scanf("%f", &score); newStudent->id = id; /* 注意字符串赋值的方式 */ strcpy(newStudent->name, name); newStudent->score = score; // 尾插法:将结点插入在链表之后 if (*head == NULL) { *head = newStudent; } else { Student *temp = *head; while (temp->next != NULL) { temp = temp->next; } temp->next = newStudent; } } // 删除学生信息 void deleteStudent(Student **head) { int id; Student *temp = *head; // 待删除结点的前一个结点,用于指向待删除结点的下一个结点 Student *prev = NULL; printf("请输入要删除的学生id: "); scanf("%d", &id); // 遍历链表,查找要删除的学生结点位置 while (temp != NULL && temp->id != id) { prev = temp; temp = temp->next; } // 如果找到了要删除的学生,从链表中删除它 if (temp == NULL) { printf("未找到学生id为%d的记录。\n", id); return; } else { if (prev == NULL) { // 删除头节点 *head = temp->next; } else { prev->next = temp->next; } free(temp); printf("已删除学生id为%d的记录。\n", id); } } // 修改学生信息 void updateStudent(Student **head) { int id; Student *temp = *head; printf("请输入要修改信息的学生id: "); scanf("%d", &id); while (temp != NULL && temp->id != id) { temp = temp->next; } // 要么链表为空,要么已经到链表尾部,这两种情况都是未找到id if (temp == NULL) { printf("未找到学生id为%d的记录。\n", id); } else { printf("请输入新的姓名和成绩:\n"); scanf("%s %f", temp->name, &temp->score); printf("已更新学生id为%d的信息。\n", id); } } // 打印学生信息 void printStudent(Student **head) { if (*head == NULL) { printf("暂未录入学生信息\n"); return; } printf("\n学生信息如下:\n"); Student *temp = *head; while (temp != NULL) { printf("ID: %d\t 姓名: %s\t 成绩: %.2f\n", temp->id, temp->name, temp->score); temp = temp->next; } } // 释放内存空间 void freeMemory(Student *head) { Student *temp; while (head != NULL) { temp = head; head = head->next; free(temp); } } // 判断链表中是否存在指定学生id int isExistId(Student **head, int id) { Student *temp = *head; while (temp != NULL) { if (temp->id == id) { return 1; } temp = temp->next; } return 0; } void searchStudent(Student **head) { int id; Student *temp = *head; printf("输入待搜索学生的id: "); scanf("%d", &id); while(temp != NULL && temp->id != id) { temp = temp->next; } if (temp == NULL) { printf("查无此id:%d信息。\n", id); } else { printf("id: %d name: %s score: %.2f\n", id, temp->name, temp->score); } }