项目概述
本项目旨在用C语言开发一个控制台(命令行)版的学生成绩管理系统,系统主要功能包括:

(图片来源网络,侵删)
- 录入学生信息:包括学号、姓名、多门课程的成绩。
- 显示所有学生信息:以表格形式展示所有学生的数据。
- 根据学号查询学生:快速定位并显示特定学生的信息。
- 根据学号删除学生:从系统中移除指定学生的记录。
- 计算并显示学生总分与平均分:为每个学生和全体学生提供统计信息。
- 数据持久化:将学生信息保存到文件中,程序关闭后数据不丢失。
核心设计思想
- 数据结构:使用
struct(结构体)来定义一个学生的信息,包括学号、姓名和成绩数组。 - 数据存储:使用一个结构体数组
struct Student students[MAX_STUDENTS]来在内存中存储所有学生信息。 - 文件操作:使用C语言的文件I/O函数 (
fopen,fread,fwrite,fclose等) 将内存中的学生数据保存到磁盘文件(如students.dat),并在程序启动时从文件加载数据。 - 模块化设计:将每个功能(如录入、显示、查询等)封装成独立的函数,使代码结构清晰,易于维护和扩展。
- 用户交互:通过
switch-case语句构建一个文本菜单,引导用户选择操作。
完整代码实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h> // 用于 getch(),实现无回显输入,非标准C,但Windows常用
// --- 常量定义 ---
#define MAX_STUDENTS 100
#define MAX_NAME_LEN 50
#define MAX_COURSES 5
#define FILENAME "students.dat"
// --- 学生结构体定义 ---
struct Student {
char id[20]; // 学号
char name[MAX_NAME_LEN];// 姓名
float scores[MAX_COURSES]; // 课程成绩
float total; // 总分
float average; // 平均分
};
// --- 全局变量 ---
struct Student students[MAX_STUDENTS];
int student_count = 0; // 当前学生数量
// --- 函数声明 ---
void show_menu();
void load_data();
void save_data();
void add_student();
void display_students();
void search_student();
void delete_student();
void calculate_statistics();
// --- 主函数 ---
int main() {
load_data(); // 程序启动时加载数据
int choice;
do {
show_menu();
printf("请输入您的选择 (0-6): ");
scanf("%d", &choice);
// 清除输入缓冲区,防止后续输入被干扰
while (getchar() != '\n');
switch (choice) {
case 1: add_student(); break;
case 2: display_students(); break;
case 3: search_student(); break;
case 4: delete_student(); break;
case 5: calculate_statistics(); break;
case 6: save_data(); printf("数据已保存,程序退出,\n"); break;
case 0: printf("感谢使用,再见!\n"); break;
default: printf("无效的输入,请重新输入!\n");
}
if (choice != 0) {
printf("\n按任意键返回主菜单...");
getch(); // 等待用户按键
}
} while (choice != 0);
return 0;
}
// --- 功能函数实现 ---
// 显示主菜单
void show_menu() {
system("cls"); // 清屏 (Windows系统), Linux下可用 system("clear");
printf("========== 学生成绩管理系统 ==========\n");
printf("|| ||\n");
printf("|| 1. 录入学生信息 ||\n");
printf("|| 2. 显示所有学生信息 ||\n");
printf("|| 3. 查询学生信息 ||\n");
printf("|| 4. 删除学生信息 ||\n");
printf("|| 5. 计算统计信息 ||\n");
printf("|| 6. 保存数据并退出 ||\n");
printf("|| 0. 退出 (不保存) ||\n");
printf("|| ||\n");
printf("======================================\n");
}
// 从文件加载数据
void load_data() {
FILE *fp = fopen(FILENAME, "rb");
if (fp == NULL) {
// 文件不存在是正常情况(首次运行)
printf("未找到数据文件,将创建新文件,\n");
student_count = 0;
return;
}
// 读取学生数量
fread(&student_count, sizeof(int), 1, fp);
// 读取所有学生数据
fread(students, sizeof(struct Student), student_count, fp);
fclose(fp);
printf("成功加载 %d 名学生的数据,\n", student_count);
}
// 保存数据到文件
void save_data() {
FILE *fp = fopen(FILENAME, "wb");
if (fp == NULL) {
printf("错误:无法打开文件进行保存!\n");
return;
}
// 先写入学生数量
fwrite(&student_count, sizeof(int), 1, fp);
// 再写入所有学生数据
fwrite(students, sizeof(struct Student), student_count, fp);
fclose(fp);
}
// 1. 录入学生信息
void add_student() {
if (student_count >= MAX_STUDENTS) {
printf("学生数量已达上限,无法继续添加!\n");
return;
}
struct Student s;
printf("--- 录入第 %d 名学生信息 ---\n", student_count + 1);
printf("请输入学号: ");
scanf("%s", s.id);
printf("请输入姓名: ");
scanf("%s", s.name);
s.total = 0;
printf("请输入 %d 门课程的成绩:\n", MAX_COURSES);
for (int i = 0; i < MAX_COURSES; i++) {
printf(" 课程 %d: ", i + 1);
scanf("%f", &s.scores[i]);
s.total += s.scores[i];
}
s.average = s.total / MAX_COURSES;
students[student_count++] = s;
printf("学生信息添加成功!\n");
}
// 2. 显示所有学生信息
void display_students() {
if (student_count == 0) {
printf("当前没有学生信息!\n");
return;
}
printf("--- 所有学生信息 ---\n");
printf("----------------------------------------------------------------------------------------\n");
printf("学号\t\t姓名\t\t");
for(int i = 0; i < MAX_COURSES; i++) printf("课程%d\t\t", i+1);
printf("总分\t\t平均分\n");
printf("----------------------------------------------------------------------------------------\n");
for (int i = 0; i < student_count; i++) {
printf("%s\t\t%s\t\t", students[i].id, students[i].name);
for (int j = 0; j < MAX_COURSES; j++) {
printf("%.2f\t\t", students[i].scores[j]);
}
printf("%.2f\t\t%.2f\n", students[i].total, students[i].average);
}
printf("----------------------------------------------------------------------------------------\n");
printf("共 %d 名学生,\n", student_count);
}
// 3. 根据学号查询学生
void search_student() {
if (student_count == 0) {
printf("当前没有学生信息!\n");
return;
}
char search_id[20];
printf("请输入要查询的学生学号: ");
scanf("%s", search_id);
for (int i = 0; i < student_count; i++) {
if (strcmp(students[i].id, search_id) == 0) {
printf("--- 查询结果 ---\n");
printf("学号: %s\n", students[i].id);
printf("姓名: %s\n", students[i].name);
printf("成绩: ");
for (int j = 0; j < MAX_COURSES; j++) {
printf("课程%d: %.2f ", j + 1, students[i].scores[j]);
}
printf("\n总分: %.2f\n", students[i].total);
printf("平均分: %.2f\n", students[i].average);
return; // 找到后直接返回
}
}
printf("未找到学号为 %s 的学生!\n", search_id);
}
// 4. 根据学号删除学生
void delete_student() {
if (student_count == 0) {
printf("当前没有学生信息!\n");
return;
}
char delete_id[20];
printf("请输入要删除的学生学号: ");
scanf("%s", delete_id);
for (int i = 0; i < student_count; i++) {
if (strcmp(students[i].id, delete_id) == 0) {
// 将后面的所有学生前移一位,覆盖掉当前学生
for (int j = i; j < student_count - 1; j++) {
students[j] = students[j + 1];
}
student_count--;
printf("学号为 %s 的学生信息已删除!\n", delete_id);
return; // 删除后直接返回
}
}
printf("未找到学号为 %s 的学生!\n", delete_id);
}
// 5. 计算统计信息
void calculate_statistics() {
if (student_count == 0) {
printf("当前没有学生信息!\n");
return;
}
float course_sums[MAX_COURSES] = {0};
float class_total_sum = 0;
float class_average_sum = 0;
for (int i = 0; i < student_count; i++) {
class_total_sum += students[i].total;
class_average_sum += students[i].average;
for (int j = 0; j < MAX_COURSES; j++) {
course_sums[j] += students[i].scores[j];
}
}
printf("--- 全班统计信息 ---\n");
printf("班级总人数: %d\n", student_count);
printf("班级总分平均: %.2f\n", class_total_sum / student_count);
printf("班级平均分平均: %.2f\n", class_average_sum / student_count);
printf("\n各课程平均分:\n");
for (int i = 0; i < MAX_COURSES; i++) {
printf(" 课程 %d 平均分: %.2f\n", i + 1, course_sums[i] / student_count);
}
}
代码解释
-
宏定义:
MAX_STUDENTS:限制系统最多能管理的学生数量。MAX_NAME_LEN:限制学生姓名的最大长度。MAX_COURSES:定义课程数量,方便修改。FILENAME:指定数据存储的文件名。
-
struct Student:定义了学生的完整数据模型,包括基本信息(学号、姓名)和成绩信息(成绩数组、总分、平均分),将总分和平均分也存入结构体,可以避免每次显示时重复计算。
-
main()函数:
(图片来源网络,侵删)- 程序的入口,首先调用
load_data()从文件加载数据。 - 使用一个
do-while循环来持续显示菜单并接收用户输入。 switch-case语句根据用户的选择调用相应的功能函数。getch()用于暂停程序,让用户看清操作结果后再返回菜单。
- 程序的入口,首先调用
-
数据持久化函数 (
load_data,save_data):load_data():以二进制读取模式 ("rb") 打开文件,如果文件不存在,fopen返回NULL,此时我们认为是首次运行,不进行任何操作,如果文件存在,先读取一个int类型的数据(即学生数量),然后根据这个数量读取相应数量的struct Student结构体数据到students数组中。save_data():以二进制写入模式 ("wb") 打开文件("wb"会覆盖原有文件),先将学生数量写入,然后将整个students数组写入文件,这种方式效率很高。
-
核心功能函数:
add_student():提示用户输入学生信息,计算总分和平均分,然后将新学生添加到students数组的末尾,并增加student_count。display_students():使用printf的格式化输出功能,以美观的表格形式打印所有学生信息。%.2f用于控制浮点数输出两位小数。search_student()&delete_student():都使用strcmp函数来比较学号字符串是否匹配,查找找到后直接显示;删除找到后,通过一个for循环将后面的元素前移,实现“删除”效果,并减少student_count。calculate_statistics():遍历所有学生,累加每个学生的总分、平均分以及每门课程的总分,最后计算出全班的各项平均值。
如何编译和运行
- 将以上代码保存为一个文件,
student_management.c。 - 打开命令行工具(如 Windows 的 CMD 或 PowerShell,Linux 的 Terminal)。
- 使用 GCC 编译器进行编译:
gcc student_management.c -o student_management.exe
- 在 Linux/macOS 上,可以去掉
.exe。
- 在 Linux/macOS 上,可以去掉
- 运行生成的可执行文件:
./student_management.exe
注意:代码中使用了 conio.h 库中的 getch() 和 system("cls"),它们在 Windows 上是兼容的,如果你在 Linux 或 macOS 上编译,需要做如下修改:
- 将
#include <conio.h>删除。 - 将
system("cls");替换为system("clear");。 - 将
getch();替换为printf("按回车键继续...");,然后使用getchar();等待用户输入。
项目扩展思路
这个基础版本已经可以满足课程设计的基本要求,如果你想让它更完善,可以考虑以下扩展:

(图片来源网络,侵删)
-
排序功能:
- 增加一个菜单项“按总分排序”。
- 使用
qsort函数(C标准库函数)对学生数组进行排序,你需要自己写一个比较函数,compare_by_total,用于比较两个学生的总分。
-
修改学生信息:
- 增加一个菜单项“修改学生信息”。
- 流程与查询类似:先找到学生,然后允许用户重新输入该学生的姓名和各科成绩,并重新计算其总分和平均分。
-
更友好的输入:
- 在录入成绩时,增加输入校验,例如防止输入负数或超过百分比的无效成绩。
- 使用
fgets代替scanf来读取字符串(如姓名),这样可以避免缓冲区溢出问题,但需要额外处理换行符。
-
图形用户界面:
- 如果想挑战更高难度,可以使用第三方库(如
EasyX在Windows上,或GTK/Qt跨平台)来创建图形界面,这将使程序更直观易用。
- 如果想挑战更高难度,可以使用第三方库(如
-
动态内存分配:
- 目前使用固定大小的数组,当学生数量接近上限时会有问题,可以改用动态内存分配(
malloc,realloc,free),根据实际学生数量动态调整内存,实现更灵活的管理。
- 目前使用固定大小的数组,当学生数量接近上限时会有问题,可以改用动态内存分配(
这个项目是学习C语言指针、结构体、数组、文件操作等核心知识的绝佳实践,希望这份详细的代码和解释能帮助你顺利完成!
