C语言程序设计实验报告
| 实验项目名称 | [学生成绩管理系统] | 实验日期 | [2025年10月26日] |
|---|---|---|---|
| 学生姓名 | [你的姓名] | 学 号 | [你的学号] |
| 班 级 | [你的班级] | 指导教师 | [教师姓名] |
实验目的
- 掌握C语言基本语法:熟练掌握变量、数据类型、运算符、表达式的基本使用。
- 掌握流程控制结构:熟练运用
if-else、switch、for、while、do-while等语句进行逻辑控制和循环处理。 - 掌握数组的使用:理解一维数组和二维数组的定义、初始化、引用方法,并能解决与数组相关的实际问题。
- 掌握函数的定义与调用:学会设计自定义函数,理解参数传递(值传递)和返回值,实现模块化编程。
- 掌握指针的应用:理解指针的概念,学会使用指针变量、指针数组、指针函数等,并能通过指针操作数组。
- 掌握结构体的使用:学会定义结构体类型,使用结构体变量存储复杂数据,并理解结构体数组的应用。
- 掌握文件操作:学会使用
fopen,fclose,fscanf,fprintf,fread,fwrite等函数进行文件的读写操作,实现数据的持久化存储。 - 培养分析问题和解决问题的能力:能够根据具体需求,设计合理的算法和数据结构,并最终通过C语言程序实现。
实验环境
- 硬件环境:[Intel Core i5 处理器, 8GB 内存]
- 软件环境:
- 操作系统:[Windows 11 / macOS Monterey]
- 编译环境:[Visual Studio 2025 / Dev-C++ 5.11 / GCC (Linux)]
- 版本信息:[MSVC v19.34 / GCC 11.2.0]
实验内容与要求
[请在此处详细描述你的实验任务。**] 设计一个简单的学生成绩管理系统,要求能够实现以下功能:

(图片来源网络,侵删)
- 录入功能:能够从键盘输入学生的学号、姓名和三门课程(C语言、高等数学、英语)的成绩。
- 显示功能:能够将所有学生的信息以表格形式在屏幕上显示出来。
- 查询功能:能够根据学号查询某个学生的具体信息,并显示。
- 修改功能:能够根据学号找到学生,并修改其某门课程的成绩。
- 删除功能:能够根据学号删除某个学生的记录。
- 保存功能:将所有学生信息保存到一个名为
students.dat的二进制文件中。 - 读取功能:程序启动时,能够从
students.dat文件中读取已保存的学生信息。
实验要求:
- 使用结构体数组来存储多个学生的信息。
- 程序应具备清晰的菜单界面,用户可以通过输入数字选择不同的功能。
- 各个功能模块应封装成独立的函数,实现模块化设计。
- 对文件操作进行错误处理(如文件打开失败等)。
- 程序运行界面友好,提示信息清晰。
实验原理
本实验主要基于C语言的以下核心知识点:
- 结构体:为了存储每个学生的多种不同类型的数据(学号、姓名、多门成绩),使用
struct关键字定义一个自定义的Student数据类型,将相关信息封装在一起。 - 数组:使用结构体数组
struct Student students[N]来存储多个学生的记录,实现批量数据的处理。 - 函数:将每个功能(如
addStudent,displayStudents,searchStudent等)编写成一个独立的函数,主函数main通过调用这些函数来组织整个程序的流程,体现了“分而治之”的模块化思想。 - 指针:在函数间传递结构体数组时,使用指针可以避免整个数组的拷贝,提高效率。
void displayStudents(struct Student *stu, int count)。 - 文件I/O:使用
fopen()以二写模式("wb")打开文件,使用fwrite()将结构体数组一次性写入文件,使用fopen()以二读模式("rb")打开文件,使用fread()将文件内容一次性读入结构体数组,这种方式高效且能保持数据结构。 - 循环与选择结构:使用
while或for循环实现菜单的反复显示和选择,使用switch-case语句根据用户的选择执行不同的功能分支。
实验步骤与过程
-
需求分析与设计:
- 定义
Student结构体,包含id(学号,char[20]),name(姓名,char[50]),score_c(C语言成绩,float),score_math(高数成绩,float),score_eng(英语成绩,float) 等成员。 - 设计主菜单,包含“1. 录入”、“2. 显示”、“3. 查询”、“4. 修改”、“5. 删除”、“6. 保存”、“7. 读取”、“0. 退出”等选项。
- 规划函数模块:
main(),showMenu(),addStudent(),displayStudents(),searchStudent(),modifyStudent(),deleteStudent(),saveToFile(),loadFromFile()。
- 定义
-
编码实现:
(图片来源网络,侵删)- 创建项目,编写
main函数,包含主循环while(1)和switch-case结构。 - 依次实现各个功能函数。
- 在
addStudent函数中,提示用户输入信息并赋值给结构体数组元素。 - 在
displayStudents函数中,使用循环遍历数组,并用printf格式化输出。 - 在
searchStudent函数中,遍历数组,比较学号,找到后返回索引。 - 在
modifyStudent和deleteStudent函数中,先调用searchStudent找到目标索引,再进行修改或删除操作(删除操作通常是将后一个元素前移)。 - 在
saveToFile函数中,以二进制写模式打开文件,检查文件指针是否为NULL,然后使用fwrite将整个数组写入。 - 在
loadFromFile函数中,以二进制读模式打开文件,使用fread读取数据到数组,并返回实际读取的学生数量。
- 创建项目,编写
-
编译与调试:
- 使用编译器(如VS/Dev-C++)编译源代码,检查并修复语法错误。
- 运行程序,测试每个功能,录入数据后,选择“显示”功能看是否正确;录入后选择“保存”,再重新运行程序看是否自动“读取”到了数据。
- 使用调试器(如VS的断点、监视窗口)跟踪变量值,定位逻辑错误(如查询不到数据、修改无效等)。
-
测试与优化:
- 进行边界测试,如删除最后一个学生、修改不存在的学号等。
- 优化用户提示信息,使其更友好。
- 检查内存泄漏等问题(本实验中主要关注文件和数组操作)。
核心代码与注释
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 定义学生结构体
#define MAX_STUDENTS 100
#define FILENAME "students.dat"
struct Student {
char id[20]; // 学号
char name[50]; // 姓名
float score_c; // C语言成绩
float score_math; // 高数成绩
float score_eng; // 英语成绩
};
struct Student students[MAX_STUDENTS];
int student_count = 0; // 当前学生人数
// 函数声明
void showMenu();
void addStudent();
void displayStudents();
void searchStudent();
void modifyStudent();
void deleteStudent();
void saveToFile();
void loadFromFile();
int main() {
int choice;
loadFromFile(); // 程序启动时加载数据
while (1) {
showMenu();
printf("请输入您的选择: ");
scanf("%d", &choice);
getchar(); // 清除输入缓冲区中的换行符
switch (choice) {
case 1: addStudent(); break;
case 2: displayStudents(); break;
case 3: searchStudent(); break;
case 4: modifyStudent(); break;
case 5: deleteStudent(); break;
case 6: saveToFile(); break;
case 0:
saveToFile(); // 退出前保存
printf("感谢使用,再见!\n");
exit(0);
default:
printf("无效的输入,请重新选择!\n");
}
}
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(" 0. 退出系统\n");
printf("====================================\n");
}
// 录入学生信息
void addStudent() {
if (student_count >= MAX_STUDENTS) {
printf("学生数量已达上限,无法继续添加!\n");
return;
}
printf("请输入学号: ");
scanf("%s", students[student_count].id);
printf("请输入姓名: ");
scanf("%s", students[student_count].name);
printf("请输入C语言成绩: ");
scanf("%f", &students[student_count].score_c);
printf("请输入高等数学成绩: ");
scanf("%f", &students[student_count].score_math);
printf("请输入英语成绩: ");
scanf("%f", &students[student_count].score_eng);
student_count++;
printf("学生信息添加成功!\n");
}
// 显示所有学生信息
void displayStudents() {
if (student_count == 0) {
printf("暂无学生信息!\n");
return;
}
printf("\n%-15s %-20s %-10s %-10s %-10s\n", "学号", "姓名", "C语言", "高数", "英语");
printf("------------------------------------------------\n");
for (int i = 0; i < student_count; i++) {
printf("%-15s %-20s %-10.2f %-10.2f %-10.2f\n",
students[i].id, students[i].name,
students[i].score_c, students[i].score_math, students[i].score_eng);
}
}
// 根据学号查找学生,返回索引,未找到返回-1
int findStudentById(const char *id) {
for (int i = 0; i < student_count; i++) {
if (strcmp(students[i].id, id) == 0) {
return i;
}
}
return -1;
}
// 查询学生信息
void searchStudent() {
char id[20];
printf("请输入要查询的学号: ");
scanf("%s", id);
int index = findStudentById(id);
if (index == -1) {
printf("未找到学号为 %s 的学生!\n", id);
} else {
printf("\n%-15s %-20s %-10s %-10s %-10s\n", "学号", "姓名", "C语言", "高数", "英语");
printf("------------------------------------------------\n");
printf("%-15s %-20s %-10.2f %-10.2f %-10.2f\n",
students[index].id, students[index].name,
students[index].score_c, students[index].score_math, students[index].score_eng);
}
}
// 修改学生信息
void modifyStudent() {
char id[20];
printf("请输入要修改的学号: ");
scanf("%s", id);
int index = findStudentById(id);
if (index == -1) {
printf("未找到学号为 %s 的学生!\n", id);
} else {
printf("找到学生 %s,请输入新的信息:\n", students[index].name);
printf("请输入新的C语言成绩 (原: %.2f): ", students[index].score_c);
scanf("%f", &students[index].score_c);
printf("请输入新的高等数学成绩 (原: %.2f): ", students[index].score_math);
scanf("%f", &students[index].score_math);
printf("请输入新的英语成绩 (原: %.2f): ", students[index].score_eng);
scanf("%f", &students[index].score_eng);
printf("学生信息修改成功!\n");
}
}
// 删除学生信息
void deleteStudent() {
char id[20];
printf("请输入要删除的学号: ");
scanf("%s", id);
int index = findStudentById(id);
if (index == -1) {
printf("未找到学号为 %s 的学生!\n", id);
} else {
// 将后面的元素前移
for (int i = index; i < student_count - 1; i++) {
students[i] = students[i + 1];
}
student_count--;
printf("学号为 %s 的学生信息已删除!\n", id);
}
}
// 保存数据到文件
void saveToFile() {
FILE *fp = fopen(FILENAME, "wb");
if (fp == NULL) {
printf("无法打开文件 %s 进行写入!\n", FILENAME);
return;
}
// 一次写入整个数组
fwrite(students, sizeof(struct Student), student_count, fp);
fclose(fp);
printf("数据已成功保存到 %s!\n", FILENAME);
}
// 从文件加载数据
void loadFromFile() {
FILE *fp = fopen(FILENAME, "rb");
if (fp == NULL) {
// 文件不存在不是错误,可能是第一次运行
printf("未找到数据文件,将创建新文件,\n");
return;
}
// 一次读取整个数组
student_count = fread(students, sizeof(struct Student), MAX_STUDENTS, fp);
fclose(fp);
printf("数据已从 %s 成功加载!共 %d 条记录,\n", FILENAME, student_count);
}
实验结果与分析
运行结果截图:
[请在此处附上你的程序运行截图,建议使用多张截图展示不同功能。]
- 截图1:主菜单界面 (展示程序启动后显示的菜单)
- 截图2:录入并显示学生信息 (展示输入数据后,选择“显示”功能输出的表格)
- 截图3:查询功能 (展示输入一个存在的学号后,显示该学生信息)
- 截图4:修改功能 (展示修改一个学生成绩后,再次查询确认修改成功)
- 截图5:保存与读取功能 (展示执行“保存”后,退出程序再重新进入,数据依然存在)
结果分析: 从运行结果可以看出,本程序成功实现了所有要求的功能:
- 用户可以通过菜单选择进行各种操作,交互界面清晰。
- 录入的学生信息能够被正确地存储在结构体数组中。
- 显示功能能够以整齐的表格形式输出所有学生数据。
- 查询、修改、删除功能都能准确地根据学号找到目标学生并进行相应操作。
- 数据保存功能成功将内存中的学生信息写入了
students.dat文件。 - 数据读取功能在程序重启时能够从文件中恢复之前的数据,实现了数据的持久化。
遇到的问题与解决方案:
- 问题:在
scanf读取字符串后,getchar()无法清除后续的换行符,导致菜单选择被跳过。 解决方案:在scanf后调用getchar()来吸收换行符,对于数字输入,scanf本身会消耗换行符,但对于字符串输入,必须手动处理。 - 问题:删除学生记录时,直接将数组大小减一,但没有处理数组元素,导致数据错乱。
解决方案:采用元素前移的方式,将被删除元素之后的所有记录向前移动一位,然后将
student_count减一。 - 问题:文件读写时,没有检查
fopen的返回值,如果文件因权限问题无法打开,程序会崩溃。 解决方案:增加if (fp == NULL)的判断,并在文件打开失败时给出明确的错误提示,而不是直接进行读写操作。
实验总结与心得
通过本次实验,我系统地复习和巩固了C语言的核心知识点,包括结构体、数组、函数、指针和文件操作,我深刻体会到模块化编程的重要性,它将一个复杂的系统分解成一个个功能独立、易于理解和维护的小模块,大大降低了编程的难度。
在设计过程中,我学会了如何从需求出发,设计合理的数据结构(使用结构体)和算法(查找、删除、修改),在编码实现时,我更加注重代码的规范性和健壮性,例如增加了对文件操作失败的处理,以及对用户非法输入的提示。
最大的收获是理解了指针在函数间传递复杂数据时的高效性,以及文件I/O在实现数据持久化方面的关键作用,这次实验不仅提升了我的编程技能,更锻炼了我分析问题、设计解决方案和动手调试的综合能力,在未来的学习中,我将更加注重代码的优化和用户体验,力求写出更高质量、更完善的程序。
