- 结构体:用于定义学生信息的结构。
- 文件操作:将学生数据持久化存储到文件中,程序关闭后数据不丢失。
- 核心功能:
- 添加学生信息
- 显示所有学生信息
- 根据学号查找学生
- 根据学号删除学生
- 根据学号修改学生信息
- 用户界面:通过一个简单的菜单让用户选择操作。
程序设计思路
- 数据结构:使用
struct定义一个Student结构体,包含学号、姓名、年龄、性别和成绩等字段。 - 数据存储:使用二进制文件(
"students.dat")来存储学生数据,二进制文件读写效率高,且结构体可以直接写入和读出,非常方便。 - 功能模块化:将每个功能(如添加、显示、查找等)都写成独立的 C 函数,使代码结构清晰,易于维护。
- 主循环:在
main函数中使用一个while循环来显示菜单,并根据用户的选择调用相应的功能函数,直到用户选择退出。
完整 C 语言代码
下面是完整的、可运行的 C 语言代码,你可以直接复制到一个 .c 文件(student_management.c)中进行编译和运行。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h> // 用于 getch() 函数,实现“按任意键继续”
// 定义学生结构体
struct Student {
char id[20]; // 学号
char name[50]; // 姓名
int age; // 年龄
char gender[10]; // 性别
float score; // 成绩
};
// 函数声明
void addStudent();
void displayStudents();
void searchStudent();
void deleteStudent();
void modifyStudent();
void saveToFile(struct Student students[], int count);
int loadFromFile(struct Student students[]);
// 全局变量,用于存储学生数组和学生数量
#define MAX_STUDENTS 100
struct Student students[MAX_STUDENTS];
int student_count = 0;
// 主函数
int main() {
// 程序启动时,先从文件加载数据
student_count = loadFromFile(students);
int choice;
while (1) {
// 清屏,创建一个清爽的界面
system("cls"); // Windows系统使用 "cls",Linux/macOS使用 "clear"
printf("\n-----------------------------\n");
printf(" 学生信息管理系统 (C语言版)\n");
printf("-----------------------------\n");
printf(" 1. 添加学生信息\n");
printf(" 2. 显示所有学生\n");
printf(" 3. 查找学生信息\n");
printf(" 4. 删除学生信息\n");
printf(" 5. 修改学生信息\n");
printf(" 0. 退出系统\n");
printf("-----------------------------\n");
printf(" 请输入您的选择: ");
scanf("%d", &choice);
// 处理用户输入
switch (choice) {
case 1:
addStudent();
break;
case 2:
displayStudents();
break;
case 3:
searchStudent();
break;
case 4:
deleteStudent();
break;
case 5:
modifyStudent();
break;
case 0:
printf("感谢使用,再见!\n");
// 退出前保存数据
saveToFile(students, student_count);
exit(0); // 正常退出程序
default:
printf("无效的输入,请重新选择!\n");
break;
}
// 暂停,让用户看清结果
printf("\n按任意键返回主菜单...");
getch(); // 等待用户按下任意键
}
return 0;
}
// 1. 添加学生信息
void addStudent() {
if (student_count >= MAX_STUDENTS) {
printf("学生数量已达上限,无法添加!\n");
return;
}
struct Student s;
printf("请输入学号: ");
scanf("%s", s.id);
// 检查学号是否已存在
for (int i = 0; i < student_count; i++) {
if (strcmp(students[i].id, s.id) == 0) {
printf("错误:该学号已存在!\n");
return;
}
}
printf("请输入姓名: ");
scanf("%s", s.name);
printf("请输入年龄: ");
scanf("%d", &s.age);
printf("请输入性别: ");
scanf("%s", s.gender);
printf("请输入成绩: ");
scanf("%f", &s.score);
students[student_count++] = s;
printf("学生信息添加成功!\n");
}
// 2. 显示所有学生信息
void displayStudents() {
if (student_count == 0) {
printf("当前没有任何学生信息!\n");
return;
}
printf("\n%-15s %-20s %-10s %-10s %-10s\n", "学号", "姓名", "年龄", "性别", "成绩");
printf("------------------------------------------------\n");
for (int i = 0; i < student_count; i++) {
printf("%-15s %-20s %-10d %-10s %-10.2f\n",
students[i].id,
students[i].name,
students[i].age,
students[i].gender,
students[i].score);
}
}
// 3. 根据学号查找学生
void searchStudent() {
if (student_count == 0) {
printf("当前没有任何学生信息!\n");
return;
}
char id[20];
printf("请输入要查找的学生学号: ");
scanf("%s", id);
for (int i = 0; i < student_count; i++) {
if (strcmp(students[i].id, id) == 0) {
printf("\n找到学生信息:\n");
printf("学号: %s\n", students[i].id);
printf("姓名: %s\n", students[i].name);
printf("年龄: %d\n", students[i].age);
printf("性别: %s\n", students[i].gender);
printf("成绩: %.2f\n", students[i].score);
return; // 找到后直接返回
}
}
printf("未找到学号为 %s 的学生!\n", id);
}
// 4. 根据学号删除学生
void deleteStudent() {
if (student_count == 0) {
printf("当前没有任何学生信息!\n");
return;
}
char id[20];
printf("请输入要删除的学生学号: ");
scanf("%s", id);
for (int i = 0; i < student_count; i++) {
if (strcmp(students[i].id, id) == 0) {
// 将后面的所有学生信息前移一位,覆盖掉要删除的学生
for (int j = i; j < student_count - 1; j++) {
students[j] = students[j + 1];
}
student_count--; // 学生总数减一
printf("学号为 %s 的学生信息已删除!\n", id);
return;
}
}
printf("未找到学号为 %s 的学生,删除失败!\n", id);
}
// 5. 根据学号修改学生信息
void modifyStudent() {
if (student_count == 0) {
printf("当前没有任何学生信息!\n");
return;
}
char id[20];
printf("请输入要修改的学生学号: ");
scanf("%s", id);
for (int i = 0; i < student_count; i++) {
if (strcmp(students[i].id, id) == 0) {
printf("找到学生 %s,请输入新的信息:\n", students[i].name);
printf("请输入新姓名 (原: %s): ", students[i].name);
scanf("%s", students[i].name);
printf("请输入新年龄 (原: %d): ", students[i].age);
scanf("%d", &students[i].age);
printf("请输入新性别 (原: %s): ", students[i].gender);
scanf("%s", students[i].gender);
printf("请输入新成绩 (原: %.2f): ", students[i].score);
scanf("%f", &students[i].score);
printf("学生信息修改成功!\n");
return;
}
}
printf("未找到学号为 %s 的学生,修改失败!\n", id);
}
// 将数据保存到文件
void saveToFile(struct Student students[], int count) {
FILE *fp = fopen("students.dat", "wb"); // "wb" 表示以二进制写入模式打开文件
if (fp == NULL) {
printf("无法打开文件进行保存!\n");
return;
}
// 一次写入整个结构体数组
fwrite(students, sizeof(struct Student), count, fp);
fclose(fp);
printf("数据已成功保存到文件 students.dat,\n");
}
// 从文件加载数据
int loadFromFile(struct Student students[]) {
FILE *fp = fopen("students.dat", "rb"); // "rb" 表示以二进制读取模式打开文件
if (fp == NULL) {
// 如果文件不存在,则返回0,表示没有数据
return 0;
}
// 一次读取整个结构体数组
int count = fread(students, sizeof(struct Student), MAX_STUDENTS, fp);
fclose(fp);
printf("数据已从文件 students.dat 加载,共 %d 条记录,\n", count);
return count;
}
如何编译和运行
- 保存代码:将上面的代码保存为
student_management.c。 - 编译:
- 如果你使用的是 GCC 编译器(在 Linux, macOS 或 Windows 的 MinGW/Cygwin 中),打开终端或命令提示符,进入文件所在目录,然后运行:
gcc student_management.c -o student_management
- 如果你使用的是 Visual Studio,创建一个新的 C++ 控制台项目,并将代码粘贴到
.c文件中,然后生成解决方案。
- 如果你使用的是 GCC 编译器(在 Linux, macOS 或 Windows 的 MinGW/Cygwin 中),打开终端或命令提示符,进入文件所在目录,然后运行:
- 运行:
- 在 GCC 环境下,运行生成的可执行文件:
./student_management
- 在 Windows (GCC) 环境下:
student_management.exe
- 在 Visual Studio 中,按
F5或点击“开始调试”按钮。
- 在 GCC 环境下,运行生成的可执行文件:
代码讲解
struct Student:这是核心数据结构,定义了每个学生包含哪些信息。main()函数:程序入口,它首先调用loadFromFile从文件加载数据,然后进入一个无限循环来显示菜单和处理用户输入。system("cls")用于清屏,getch()用于实现“按任意键继续”的交互。addStudent():提示用户输入学生信息,并检查学号是否重复,如果一切正常,则将新学生添加到students数组的末尾,并增加student_count。displayStudents():遍历students数组,使用printf的格式化输出功能,以表格形式展示所有学生信息。searchStudent():遍历数组,通过strcmp函数比较学号,找到后打印该学生的详细信息。deleteStudent():遍历数组找到目标学生,然后使用一个for循环将目标学生之后的所有元素向前移动一位,从而覆盖掉要删除的学生,最后将student_count减一。modifyStudent():找到目标学生后,允许用户重新输入该学生的各项信息,直接覆盖数组中原有的数据。saveToFile()和loadFromFile():fopen("students.dat", "wb"):以二进制写入模式打开文件,如果文件不存在,会创建一个新文件。fwrite(students, sizeof(struct Student), count, fp):这是关键,它将students数组中count个struct Student大小的数据块一次性写入文件,效率很高。fopen("students.dat", "rb"):以二进制读取模式打开文件。fread(students, sizeof(struct Student), MAX_STUDENTS, fp):一次性从文件中读取最多MAX_STUDENTS个学生结构体到students数组中。- 使用
fclose(fp)关闭文件是一个好习惯,可以释放资源。
可能的改进方向
这个版本已经是一个功能完整的管理系统,但如果你想进一步学习和改进,可以考虑以下几点:
- 动态内存分配:目前使用固定大小的数组 (
MAX_STUDENTS),限制了学生数量,可以使用malloc、realloc和free来动态分配内存,实现无限添加学生。 - 排序功能:增加一个“按成绩排序”或“按学号排序”的功能,可以使用
qsort函数(C标准库中的快速排序函数)来实现。 - 更健壮的输入验证:检查年龄是否为正数,成绩是否在0-100之间等,防止用户输入无效数据。
- 更友好的界面:使用颜色或更复杂的布局来美化控制台界面。
- 数据加密:如果对安全性要求高,可以在写入文件前对数据进行简单的加密,读出时再解密。
- 多文件组织:将函数声明放在
.h头文件中,函数实现在.c源文件中,使项目结构更规范。
