这个设计将遵循软件工程的最佳实践,包括:

(图片来源网络,侵删)
- 模块化设计:将不同功能(如输入、显示、排序、保存)封装成独立的函数。
- 数据结构:使用结构体来存储学生信息,使用动态数组来管理学生列表,以支持可变数量的学生。
- 文件持久化:将学生数据保存到文件中,程序关闭后重新打开时数据不会丢失。
- 用户友好界面:提供清晰的菜单,引导用户进行操作。
- 健壮性:处理用户输入错误等异常情况。
系统需求分析
一个基本的学生信息管理系统应具备以下核心功能:
- 添加学生信息:包括学号、姓名、性别、年龄、成绩(多门)等。
- 显示所有学生信息:以表格形式清晰展示所有学生的数据。
- 查询学生信息:按学号或姓名查询,并显示查询结果。
- 修改学生信息:根据学号找到学生并修改其信息。
- 删除学生信息:根据学号或姓名删除指定学生。
- 统计信息:计算并显示平均分、最高分、最低分等。
- 数据排序:按成绩或学号对学生列表进行排序。
- 数据保存与加载:将数据保存到文件,并能在程序启动时从文件加载数据。
数据结构设计
为了存储学生信息,我们定义一个结构体 Student。
// student.h - 头文件,用于声明常量和结构体
#ifndef STUDENT_H
#define STUDENT_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 定义最大学生数量(如果使用静态数组)
// #define MAX_STUDENTS 100
// 定义科目数量
#define SUBJECT_COUNT 3
// 学生结构体
typedef struct {
char id[20]; // 学号
char name[50]; // 姓名
int age; // 年龄
char gender[10]; // 性别
float scores[SUBJECT_COUNT]; // 科目成绩数组
float average; // 平均分
} Student;
#endif // STUDENT_H
设计说明:
typedef struct Student Student;:为struct Student创建一个别名Student,使代码更简洁。- 使用字符数组
char[]来存储字符串(如学号、姓名),因为它们长度固定且明确。 scores数组用于存储多门成绩,便于扩展。average字段用于缓存计算好的平均分,避免每次显示时都重新计算,提高效率。
功能模块设计
我们将系统功能分解为多个函数,每个函数负责一个特定的任务。

(图片来源网络,侵删)
1 主菜单函数
void showMenu();:显示系统菜单,接收用户输入并调用相应的功能函数。
2 核心功能函数
这些函数是系统的业务逻辑核心。
void addStudent(Student *students, int *count);:添加新学生。void displayAllStudents(const Student *students, int count);:显示所有学生。void searchStudent(const Student *students, int count);:查询学生。void modifyStudent(Student *students, int count);:修改学生信息。void deleteStudent(Student *students, int *count);:删除学生。void calculateStatistics(const Student *students, int count);:统计信息。void sortStudents(Student *students, int count);:排序学生。
3 文件操作函数
void saveToFile(const Student *students, int count);:将学生数据保存到文件(如students.dat)。void loadFromFile(Student *students, int *count);:从文件加载学生数据。
4 辅助函数
void findStudentById(const Student *students, int count, const char *id, Student **foundStudent);:通过ID查找学生,返回其指针。int findStudentIndexById(const Student *students, int count, const char *id);:通过ID查找学生,返回其索引。float calculateAverage(const float scores[]);:计算一个学生的平均分。
完整代码实现
下面是整合了所有模块的完整C语言代码。
main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "student.h" // 引入我们定义的头文件
// 函数声明
void showMenu();
void addStudent(Student *students, int *count);
void displayAllStudents(const Student *students, int count);
void searchStudent(const Student *students, int count);
void modifyStudent(Student *students, int count);
void deleteStudent(Student *students, int *count);
void calculateStatistics(const Student *students, int count);
void sortStudents(Student *students, int count);
void saveToFile(const Student *students, int count);
void loadFromFile(Student *students, int *count);
void findStudentById(const Student *students, int count, const char *id, Student **foundStudent);
int findStudentIndexById(const Student *students, int count, const char *id);
float calculateAverage(const float scores[]);
int main() {
// 使用动态数组来存储学生,初始容量为10
int capacity = 10;
Student *students = (Student *)malloc(capacity * sizeof(Student));
if (students == NULL) {
printf("内存分配失败!\n");
return 1;
}
int studentCount = 0;
int choice;
// 程序启动时尝试从文件加载数据
loadFromFile(students, &studentCount);
do {
showMenu();
printf("请输入您的选择: ");
scanf("%d", &choice);
// 清除输入缓冲区中的换行符,防止影响后续的gets或fgets
while (getchar() != '\n');
switch (choice) {
case 1:
addStudent(students, &studentCount);
break;
case 2:
displayAllStudents(students, studentCount);
break;
case 3:
searchStudent(students, studentCount);
break;
case 4:
modifyStudent(students, studentCount);
break;
case 5:
deleteStudent(students, &studentCount);
break;
case 6:
calculateStatistics(students, studentCount);
break;
case 7:
sortStudents(students, studentCount);
break;
case 8:
saveToFile(students, studentCount);
printf("数据已保存!\n");
break;
case 0:
saveToFile(students, studentCount); // 退出前自动保存
printf("感谢使用,再见!\n");
break;
default:
printf("无效的选择,请重新输入!\n");
}
} while (choice != 0);
// 释放动态分配的内存
free(students);
return 0;
}
// 显示菜单
void showMenu() {
printf("\n========== 学生信息管理系统 ==========\n");
printf(" 1. 添加学生信息\n");
printf(" 2. 显示所有学生信息\n");
printf(" 3. 查询学生信息\n");
printf(" 4. 修改学生信息\n");
printf(" 5. 删除学生信息\n");
printf(" 6. 统计信息\n");
printf(" 7. 排序学生\n");
printf(" 8. 保存数据到文件\n");
printf(" 0. 退出系统\n");
printf("=====================================\n");
}
// 添加学生
void addStudent(Student *students, int *count) {
if (*count >= 100) { // 简单限制最大数量
printf("学生数量已达上限,无法添加!\n");
return;
}
Student s;
printf("请输入学号: ");
gets(s.id);
printf("请输入姓名: ");
gets(s.name);
printf("请输入年龄: ");
scanf("%d", &s.age);
printf("请输入性别: ");
while (getchar() != '\n'); // 清除缓冲区
gets(s.gender);
float sum = 0;
printf("请输入 %d 门课程的成绩:\n", SUBJECT_COUNT);
for (int i = 0; i < SUBJECT_COUNT; i++) {
printf(" 科目 %d: ", i + 1);
scanf("%f", &s.scores[i]);
sum += s.scores[i];
}
s.average = sum / SUBJECT_COUNT;
students[*count] = s;
(*count)++;
printf("学生信息添加成功!\n");
}
// 显示所有学生
void displayAllStudents(const Student *students, int count) {
if (count == 0) {
printf("当前没有学生信息!\n");
return;
}
printf("\n%-15s %-10s %-5s %-6s", "学号", "姓名", "年龄", "性别");
for(int i = 0; i < SUBJECT_COUNT; i++) {
printf(" 科目%d", i + 1);
}
printf(" 平均分\n");
printf("------------------------------------------------------------\n");
for (int i = 0; i < count; i++) {
printf("%-15s %-10s %-5d %-6s", students[i].id, students[i].name, students[i].age, students[i].gender);
for (int j = 0; j < SUBJECT_COUNT; j++) {
printf(" %-7.1f", students[i].scores[j]);
}
printf(" %-7.1f\n", students[i].average);
}
}
// 查询学生
void searchStudent(const Student *students, int count) {
if (count == 0) {
printf("当前没有学生信息!\n");
return;
}
int choice;
printf("按什么查询?\n");
printf("1. 按学号\n");
printf("2. 按姓名\n");
printf("请选择: ");
scanf("%d", &choice);
while (getchar() != '\n');
char keyword[50];
printf("请输入查询关键词: ");
gets(keyword);
int found = 0;
if (choice == 1) {
Student *foundStudent = NULL;
findStudentById(students, count, keyword, &foundStudent);
if (foundStudent) {
printf("\n找到学生:\n");
printf("学号: %s, 姓名: %s, 年龄: %d, 性别: %s\n",
foundStudent->id, foundStudent->name, foundStudent->age, foundStudent->gender);
printf("成绩: ");
for (int i = 0; i < SUBJECT_COUNT; i++) {
printf("科目%d: %.1f ", i + 1, foundStudent->scores[i]);
}
printf("\n平均分: %.1f\n", foundStudent->average);
found = 1;
}
} else if (choice == 2) {
for (int i = 0; i < count; i++) {
if (strcmp(students[i].name, keyword) == 0) {
printf("\n找到学生:\n");
printf("学号: %s, 姓名: %s, 年龄: %d, 性别: %s\n",
students[i].id, students[i].name, students[i].age, students[i].gender);
printf("成绩: ");
for (int j = 0; j < SUBJECT_COUNT; j++) {
printf("科目%d: %.1f ", j + 1, students[i].scores[j]);
}
printf("\n平均分: %.1f\n", students[i].average);
found = 1;
}
}
} else {
printf("无效的选择!\n");
return;
}
if (!found) {
printf("未找到匹配的学生信息!\n");
}
}
// 修改学生
void modifyStudent(Student *students, int count) {
if (count == 0) {
printf("当前没有学生信息!\n");
return;
}
char id[20];
printf("请输入要修改的学生的学号: ");
gets(id);
int index = findStudentIndexById(students, count, id);
if (index == -1) {
printf("未找到学号为 %s 的学生!\n", id);
return;
}
Student *s = &students[index];
printf("找到学生: %s, %s\n", s->id, s->name);
printf("请输入新的姓名 (原: %s): ", s->name);
gets(s->name);
printf("请输入新的年龄 (原: %d): ", s->age);
scanf("%d", &s->age);
while (getchar() != '\n');
printf("请输入新的性别 (原: %s): ", s->gender);
gets(s->gender);
float sum = 0;
printf("请输入新的 %d 门课程的成绩:\n", SUBJECT_COUNT);
for (int i = 0; i < SUBJECT_COUNT; i++) {
printf(" 科目 %d (原: %.1f): ", i + 1, s->scores[i]);
scanf("%f", &s->scores[i]);
sum += s->scores[i];
}
s->average = sum / SUBJECT_COUNT;
printf("学生信息修改成功!\n");
}
// 删除学生
void deleteStudent(Student *students, int *count) {
if (*count == 0) {
printf("当前没有学生信息!\n");
return;
}
char id[20];
printf("请输入要删除的学生的学号: ");
gets(id);
int index = findStudentIndexById(students, *count, id);
if (index == -1) {
printf("未找到学号为 %s 的学生!\n", id);
return;
}
// 将后面的所有学生前移一位,覆盖掉要删除的学生
for (int i = index; i < *count - 1; i++) {
students[i] = students[i + 1];
}
(*count)--;
printf("学号为 %s 的学生信息已删除!\n", id);
}
// 统计信息
void calculateStatistics(const Student *students, int count) {
if (count == 0) {
printf("当前没有学生信息!\n");
return;
}
float totalAverage = 0;
float maxScore = students[0].average;
float minScore = students[0].average;
char topStudentName[50] = "";
float topStudentScore = students[0].average;
for (int i = 0; i < count; i++) {
totalAverage += students[i].average;
if (students[i].average > maxScore) {
maxScore = students[i].average;
strcpy(topStudentName, students[i].name);
}
if (students[i].average < minScore) {
minScore = students[i].average;
}
if (students[i].average > topStudentScore) {
topStudentScore = students[i].average;
strcpy(topStudentName, students[i].name);
}
}
printf("\n===== 统计信息 =====\n");
printf("学生总数: %d\n", count);
printf("全体平均分: %.2f\n", totalAverage / count);
printf("最高分: %.2f (学生: %s)\n", maxScore, topStudentName);
printf("最低分: %.2f\n", minScore);
printf("====================\n");
}
// 排序学生
void sortStudents(Student *students, int count) {
if (count == 0) {
printf("当前没有学生信息!\n");
return;
}
int choice;
printf("按什么排序?\n");
printf("1. 按学号 (升序)\n");
printf("2. 按平均分 (降序)\n");
printf("请选择: ");
scanf("%d", &choice);
if (choice == 1) {
// 按学号排序 (使用冒泡排序)
for (int i = 0; i < count - 1; i++) {
for (int j = 0; j < count - 1 - i; j++) {
if (strcmp(students[j].id, students[j + 1].id) > 0) {
Student temp = students[j];
students[j] = students[j + 1];
students[j + 1] = temp;
}
}
}
printf("已按学号升序排序!\n");
} else if (choice == 2) {
// 按平均分排序 (使用选择排序)
for (int i = 0; i < count - 1; i++) {
int max_idx = i;
for (int j = i + 1; j < count; j++) {
if (students[j].average > students[max_idx].average) {
max_idx = j;
}
}
if (max_idx != i) {
Student temp = students[i];
students[i] = students[max_idx];
students[max_idx] = temp;
}
}
printf("已按平均分降序排序!\n");
} else {
printf("无效的选择!\n");
return;
}
displayAllStudents(students, count); // 排序后立即显示
}
// 保存到文件
void saveToFile(const Student *students, int count) {
FILE *fp = fopen("students.dat", "wb");
if (fp == NULL) {
printf("无法打开文件进行保存!\n");
return;
}
// 先写入学生数量
fwrite(&count, sizeof(int), 1, fp);
// 再写入所有学生数据
fwrite(students, sizeof(Student), count, fp);
fclose(fp);
}
// 从文件加载
void loadFromFile(Student *students, int *count) {
FILE *fp = fopen("students.dat", "rb");
if (fp == NULL) {
// 文件不存在是正常情况(第一次运行)
printf("未找到数据文件,将创建新文件,\n");
return;
}
// 先读取学生数量
fread(count, sizeof(int), 1, fp);
// 再读取所有学生数据
fread(students, sizeof(Student), *count, fp);
fclose(fp);
printf("成功从文件加载 %d 条学生记录,\n", *count);
}
// 通过ID查找学生,返回其指针
void findStudentById(const Student *students, int count, const char *id, Student **foundStudent) {
*foundStudent = NULL;
for (int i = 0; i < count; i++) {
if (strcmp(students[i].id, id) == 0) {
*foundStudent = (Student *)&students[i];
return;
}
}
}
// 通过ID查找学生,返回其索引
int findStudentIndexById(const Student *students, int count, const char *id) {
for (int i = 0; i < count; i++) {
if (strcmp(students[i].id, id) == 0) {
return i;
}
}
return -1; // 未找到
}
float calculateAverage(const float scores[]) {
float sum = 0;
for (int i = 0; i < SUBJECT_COUNT; i++) {
sum += scores[i];
}
return sum / SUBJECT_COUNT;
}
student.h
#ifndef STUDENT_H
#define STUDENT_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 定义科目数量
#define SUBJECT_COUNT 3
// 学生结构体
typedef struct {
char id[20]; // 学号
char name[50]; // 姓名
int age; // 年龄
char gender[10]; // 性别
float scores[SUBJECT_COUNT]; // 科目成绩数组
float average; // 平均分
} Student;
#endif // STUDENT_H
如何编译和运行
- 保存文件:将上面的
main.c代码保存为main.c,将student.h代码保存为student.h,并将它们放在同一个文件夹下。 - 编译:打开终端或命令提示符,进入该文件夹,使用GCC进行编译。
gcc main.c -o student_management_system
- 运行:
- 在Windows上:
student_management_system.exe
- 在Linux或macOS上:
./student_management_system
- 在Windows上:
系统演示与扩展
演示流程:
- 启动:程序运行,会提示“未找到数据文件,将创建新文件。”
- 添加:选择
1,输入几个学生的信息。 - 显示:选择
2,查看所有学生。 - 查询:选择
3,按学号或姓名查询一个已存在的学生。 - 修改:选择
4,输入一个学号,修改该学生的某项信息。 - 统计:选择
6,查看平均分、最高分等统计信息。 - 排序:选择
7,按平均分排序后再次查看学生列表,观察顺序变化。 - 保存:选择
8,将数据保存到students.dat文件。 - 退出:选择
0,程序会再次保存并退出。 - 重启:再次运行程序,会发现之前添加的学生数据已经自动加载了。
可扩展功能:
- 密码登录:增加一个简单的用户名和密码验证功能。
- 更复杂的排序:增加按单科成绩、总分等排序选项。
- 数据验证:在输入学号、年龄、成绩时,进行格式和范围检查(如年龄不能为负,成绩在0-100之间)。
- 图形用户界面:使用如GTK、Qt等库为程序开发图形界面,提升用户体验。
- 链表存储:将静态数组或动态数组改为链表,在频繁插入和删除操作时性能更优。
- 多文件组织:将
main.c中的不同功能模块拆分到.c文件中(如input.c,display.c,file_io.c),并创建对应的.h头文件,使项目结构更清晰、更易于维护。
这个设计提供了一个功能完整、结构良好的C语言学生信息管理系统,可以作为C语言课程设计或项目实践的优秀范例。

(图片来源网络,侵删)
