C语言课程设计:学生信息管理系统
需求分析
一个功能完善的学生信息管理系统应该具备以下基本功能:

(图片来源网络,侵删)
- 数据录入:能够从键盘输入学生信息,包括学号、姓名、性别、年龄、多门课程成绩等,并将信息保存到文件中。
- 数据浏览:能够以表格形式显示所有学生的信息。
- 数据查询:能够按学号或姓名查询学生信息,并显示查询结果。
- 数据修改:能够根据学号找到学生,并修改其除学号外的任意信息。
- 数据删除:能够根据学号删除指定学生的信息。
- 数据排序:能够按照总成绩或单科成绩对学生进行排序(升序或降序)。
- 数据统计:能够计算并显示单科的平均分、最高分、最低分。
- 数据持久化:所有数据应保存在文件中(如
students.dat),程序启动时能从文件加载,退出时能自动保存,确保数据不丢失。
系统设计
数据结构设计
为了存储学生信息,我们需要定义一个结构体 Student,考虑到系统可能需要处理多门课程,我们可以使用一个数组来存储成绩。
#define MAX_NAME_LEN 20
#define MAX_COURSE_NUM 5 // 假设最多5门课程
#define FILENAME "students.dat"
typedef struct {
char id[20]; // 学号
char name[MAX_NAME_LEN]; // 姓名
char gender; // 性别 'M'/'F'
int age; // 年龄
float scores[MAX_COURSE_NUM]; // 课程成绩数组
int course_count; // 实际课程数
float total_score; // 总成绩
} Student;
功能模块设计
我们将整个程序分解为多个功能函数,每个函数负责一个特定的任务,这符合模块化编程的思想。

(图片来源网络,侵删)
| 函数名 | 功能描述 |
|---|---|
menu() |
显示主菜单,并获取用户选择 |
addStudent() |
添加新学生信息 |
displayAllStudents() |
显示所有学生信息 |
searchStudent() |
按学号或姓名查询学生 |
modifyStudent() |
修改学生信息 |
deleteStudent() |
删除学生信息 |
sortStudents() |
按成绩排序 |
statistics() |
统计成绩信息 |
saveToFile() |
将学生数组保存到文件 |
loadFromFile() |
从文件加载学生数据 |
main() |
程序入口,调用其他函数 |
文件结构设计
students.dat:二进制文件,用于持久化存储学生数据,使用二进制文件读写(fwrite,fread)比文本文件更高效,也更方便。
核心代码实现
下面是一个完整的、可运行的代码示例,你可以直接复制到你的C语言编译器(如 Dev-C++, VS Code, Code::Blocks)中运行。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h> // 用于 getch(),在Windows下清屏和暂停
// --- 全局变量 ---
#define MAX_STUDENTS 100
Student students[MAX_STUDENTS];
int student_count = 0;
// --- 函数声明 ---
void menu();
void addStudent();
void displayAllStudents();
void searchStudent();
void modifyStudent();
void deleteStudent();
void sortStudents();
void statistics();
void saveToFile();
void loadFromFile();
void clearScreen();
int main() {
loadFromFile(); // 程序启动时加载数据
int choice;
do {
menu();
printf("请输入您的选择: ");
scanf("%d", &choice);
// 清除输入缓冲区,防止输入非数字导致的问题
while(getchar() != '\n');
switch(choice) {
case 1: addStudent(); break;
case 2: displayAllStudents(); break;
case 3: searchStudent(); break;
case 4: modifyStudent(); break;
case 5: deleteStudent(); break;
case 6: sortStudents(); break;
case 7: statistics(); break;
case 0:
saveToFile();
printf("感谢使用,再见!\n");
break;
default:
printf("无效的输入,请重新选择!\n");
break;
}
if(choice != 0) {
printf("\n按任意键返回主菜单...");
getch(); // 等待用户按键
clearScreen();
}
} while(choice != 0);
return 0;
}
// 显示主菜单
void menu() {
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(" 0. 退出系统\n");
printf("=====================================\n");
}
// 添加学生
void addStudent() {
if (student_count >= MAX_STUDENTS) {
printf("学生数量已达上限,无法添加!\n");
return;
}
Student s;
printf("请输入学号: ");
scanf("%s", s.id);
printf("请输入姓名: ");
scanf("%s", s.name);
printf("请输入性别: ");
scanf(" %c", &s.gender); // 注意前面的空格,用于跳过空白字符
printf("请输入年龄: ");
scanf("%d", &s.age);
printf("请输入课程数量 (1-%d): ", MAX_COURSE_NUM);
scanf("%d", &s.course_count);
if(s.course_count <= 0 || s.course_count > MAX_COURSE_NUM) {
printf("课程数量无效!\n");
return;
}
s.total_score = 0;
for (int i = 0; i < s.course_count; i++) {
printf("请输入第%d门课程的成绩: ", i + 1);
scanf("%f", &s.scores[i]);
s.total_score += s.scores[i];
}
students[student_count++] = s;
printf("学生信息添加成功!\n");
}
// 显示所有学生
void displayAllStudents() {
if (student_count == 0) {
printf("没有学生信息!\n");
return;
}
printf("\n%-15s %-10s %-6s %-4s", "学号", "姓名", "性别", "年龄");
for(int i = 0; i < MAX_COURSE_NUM; i++) {
printf(" 课程%d", i+1);
}
printf(" 总分\n");
printf("------------------------------------------------------------\n");
for (int i = 0; i < student_count; i++) {
printf("%-15s %-10s %-6c %-4d", students[i].id, students[i].name, students[i].gender, students[i].age);
for (int j = 0; j < students[i].course_count; j++) {
printf(" %-8.1f", students[i].scores[j]);
}
printf(" %-8.1f\n", students[i].total_score);
}
}
// 查询学生
void searchStudent() {
if (student_count == 0) {
printf("没有学生信息!\n");
return;
}
int choice;
printf("1. 按学号查询\n2. 按姓名查询\n请选择查询方式: ");
scanf("%d", &choice);
while(getchar() != '\n'); // 清空缓冲区
if (choice == 1) {
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, 姓名: %s, 性别: %c, 年龄: %d\n", students[i].id, students[i].name, students[i].gender, students[i].age);
for(int j = 0; j < students[i].course_count; j++) {
printf("课程%d成绩: %.1f\n", j+1, students[i].scores[j]);
}
printf("总分: %.1f\n", students[i].total_score);
return;
}
}
printf("未找到学号为 %s 的学生!\n", id);
} else if (choice == 2) {
char name[MAX_NAME_LEN];
printf("请输入要查询的姓名: ");
scanf("%s", name);
int found = 0;
for (int i = 0; i < student_count; i++) {
if (strcmp(students[i].name, name) == 0) {
printf("\n找到学生信息:\n");
printf("学号: %s, 姓名: %s, 性别: %c, 年龄: %d\n", students[i].id, students[i].name, students[i].gender, students[i].age);
for(int j = 0; j < students[i].course_count; j++) {
printf("课程%d成绩: %.1f\n", j+1, students[i].scores[j]);
}
printf("总分: %.1f\n", students[i].total_score);
found = 1;
}
}
if (!found) {
printf("未找到姓名为 %s 的学生!\n", name);
}
} else {
printf("无效的选择!\n");
}
}
// 修改学生
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);
char input[50];
printf("姓名 [%s]: ", students[i].name);
getchar(); // 消耗掉之前的换行符
fgets(input, sizeof(input), stdin);
if (input[0] != '\n') {
input[strcspn(input, "\n")] = 0; // 去掉换行符
strcpy(students[i].name, input);
}
printf("性别 [%c]: ", students[i].gender);
fgets(input, sizeof(input), stdin);
if (input[0] != '\n') {
students[i].gender = input[0];
}
printf("年龄 [%d]: ", students[i].age);
fgets(input, sizeof(input), stdin);
if (input[0] != '\n') {
students[i].age = atoi(input);
}
// 重新计算总分
students[i].total_score = 0;
for (int j = 0; j < students[i].course_count; j++) {
printf("课程%d成绩 [%.1f]: ", j+1, students[i].scores[j]);
fgets(input, sizeof(input), stdin);
if (input[0] != '\n') {
students[i].scores[j] = atof(input);
}
students[i].total_score += students[i].scores[j];
}
printf("学生信息修改成功!\n");
return;
}
}
printf("未找到学号为 %s 的学生!\n", id);
}
// 删除学生
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);
}
// 排序学生 (按总分降序)
void sortStudents() {
if (student_count == 0) {
printf("没有学生信息!\n");
return;
}
for (int i = 0; i < student_count - 1; i++) {
for (int j = 0; j < student_count - 1 - i; j++) {
if (students[j].total_score < students[j + 1].total_score) {
Student temp = students[j];
students[j] = students[j + 1];
students[j + 1] = temp;
}
}
}
printf("已按总分降序排序!\n");
displayAllStudents();
}
// 统计信息
void statistics() {
if (student_count == 0) {
printf("没有学生信息!\n");
return;
}
int course_idx;
printf("请输入要统计的课程编号 (1-%d): ", MAX_COURSE_NUM);
scanf("%d", &course_idx);
if(course_idx < 1 || course_idx > MAX_COURSE_NUM) {
printf("无效的课程编号!\n");
return;
}
float sum = 0, max = students[0].scores[course_idx-1], min = students[0].scores[course_idx-1];
int valid_count = 0;
for(int i = 0; i < student_count; i++) {
if(students[i].course_count >= course_idx) { // 确保该学生有这门课的成绩
float score = students[i].scores[course_idx-1];
sum += score;
if(score > max) max = score;
if(score < min) min = score;
valid_count++;
}
}
if(valid_count == 0) {
printf("没有学生选修了这门课程!\n");
return;
}
printf("\n课程%d 的统计结果:\n", course_idx);
printf("平均分: %.2f\n", sum / valid_count);
printf("最高分: %.2f\n", max);
printf("最低分: %.2f\n", min);
}
// 保存到文件
void saveToFile() {
FILE *fp = fopen(FILENAME, "wb");
if (fp == NULL) {
printf("无法打开文件 %s 进行写入!\n", FILENAME);
return;
}
fwrite(&student_count, sizeof(int), 1, fp);
fwrite(students, sizeof(Student), student_count, fp);
fclose(fp);
printf("数据已成功保存到 %s\n", FILENAME);
}
// 从文件加载
void loadFromFile() {
FILE *fp = fopen(FILENAME, "rb");
if (fp == NULL) {
printf("未找到数据文件 %s,将创建一个新文件,\n", FILENAME);
return;
}
fread(&student_count, sizeof(int), 1, fp);
fread(students, sizeof(Student), student_count, fp);
fclose(fp);
printf("数据已成功从 %s 加载,\n", FILENAME);
}
// 清屏函数 (Windows)
void clearScreen() {
#ifdef _WIN32
system("cls");
#else
system("clear");
#endif
}
如何扩展和优化
这个基础版本已经可以满足课程设计的基本要求,如果你想让它更出色,可以考虑以下扩展方向:
-
动态内存分配:
(图片来源网络,侵删)- 问题:当前使用全局数组
students[MAX_STUDENTS],限制了学生数量上限。 - 方案:使用
malloc,realloc,free等动态内存管理函数,可以将Student数组声明为Student *students;,在loadFromFile或addStudent时根据需要分配和重新分配内存。
- 问题:当前使用全局数组
-
更健壮的输入验证:
- 问题:
scanf对非法输入(如输入字母给数字)处理能力弱,容易导致程序出错。 - 方案:封装一个安全的输入函数,
getIntegerInput(prompt, min, max),该函数会循环提示用户,直到输入一个合法的整数,字符串和字符输入也可以做类似处理。
- 问题:
-
更友好的用户界面:
- 问题:纯文本界面,不够美观。
- 方案:
- 使用颜色:在Windows下,可以通过
system("color XX")改变控制台颜色,或者使用SetConsoleTextAttributeAPI。 - 使用第三方库:如
ncurses(Linux/Unix) 或PDCurses(跨平台) 可以创建更复杂的文本UI,如按钮、输入框等。
- 使用颜色:在Windows下,可以通过
-
功能增强:
- 多条件排序:允许用户先按总分排序,总分相同的再按某一单科成绩排序。
- 数据导入/导出:增加将数据导出为CSV或Excel文件的功能,方便在其他软件中查看和编辑。
- 密码保护:为系统添加登录功能,设置管理员密码,防止未授权访问。
- 图形用户界面:如果学有余力,可以使用C++的Qt库或C语言的GTK库来开发一个带窗口的GUI版本,这会是巨大的加分项。
-
代码结构优化:
- 问题:所有函数都在一个文件中,对于大型项目来说难以维护。
- 方案:将代码拆分为多个文件。
student.h:存放结构体定义和函数声明。student.c:存放所有函数的实现。main.c:只包含main函数。
- 这样做符合软件工程的模块化思想。
课程设计报告撰写要点
完成代码后,你需要撰写一份课程设计报告,报告应包含以下部分:
- 封面、姓名、学号、班级、日期。
- 摘要:简要介绍项目背景、目的、主要功能和实现技术。
- 目录。
- 第一章:绪论
- 1 项目背景与意义
- 2 主要研究内容
- 第二章:需求分析
- 1 功能需求(列出你设计的所有功能)
- 2 性能需求(如响应速度、数据容量等)
- 第三章:系统设计
- 1 开发环境(如 Windows 10, Dev-C++ 5.11)
- 2 总体设计(画出系统功能模块图)
- 3 详细设计
- 3.1 数据结构设计(画出结构体定义)
- 3.2 功能模块设计(简要描述每个函数的作用)
- 第四章:系统实现
- 1 开发工具介绍
- 2 核心代码实现(附上关键代码片段,并加以注释说明)
- 第五章:系统测试
- 1 测试环境
- 2 测试用例与结果(使用表格形式,测试功能、输入数据、预期结果、实际结果、是否通过)
- 第六章:总结与展望
- 1 项目总结(完成了什么,有什么不足)
- 2 未来展望(提出可以改进和扩展的方向,如上文提到的几点)
- 致谢
- 参考文献
- 附录:完整的源代码。
希望这份详细的指南能帮助你顺利完成你的C语言课程设计!祝你成功!
