C语言实验报告如何高效撰写与避免常见错误?

99ANYc3cd6
预计阅读时长 37 分钟
位置: 首页 C语言 正文

C语言程序设计实验报告

实验项目名称 [学生成绩管理系统] 实验日期 [2025年10月26日]
学生姓名 [你的姓名] 学 号 [你的学号]
班 级 [你的班级] 指导教师 [教师姓名]

实验目的

  1. 掌握C语言基本语法:熟练掌握变量、数据类型、运算符、表达式的基本使用。
  2. 掌握流程控制结构:熟练运用 if-elseswitchforwhiledo-while 等语句进行逻辑控制和循环处理。
  3. 掌握数组的使用:理解一维数组和二维数组的定义、初始化、引用方法,并能解决与数组相关的实际问题。
  4. 掌握函数的定义与调用:学会设计自定义函数,理解参数传递(值传递)和返回值,实现模块化编程。
  5. 掌握指针的应用:理解指针的概念,学会使用指针变量、指针数组、指针函数等,并能通过指针操作数组。
  6. 掌握结构体的使用:学会定义结构体类型,使用结构体变量存储复杂数据,并理解结构体数组的应用。
  7. 掌握文件操作:学会使用 fopen, fclose, fscanf, fprintf, fread, fwrite 等函数进行文件的读写操作,实现数据的持久化存储。
  8. 培养分析问题和解决问题的能力:能够根据具体需求,设计合理的算法和数据结构,并最终通过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语言程序设计 实验报告
(图片来源网络,侵删)
  1. 录入功能:能够从键盘输入学生的学号、姓名和三门课程(C语言、高等数学、英语)的成绩。
  2. 显示功能:能够将所有学生的信息以表格形式在屏幕上显示出来。
  3. 查询功能:能够根据学号查询某个学生的具体信息,并显示。
  4. 修改功能:能够根据学号找到学生,并修改其某门课程的成绩。
  5. 删除功能:能够根据学号删除某个学生的记录。
  6. 保存功能:将所有学生信息保存到一个名为 students.dat 的二进制文件中。
  7. 读取功能:程序启动时,能够从 students.dat 文件中读取已保存的学生信息。

实验要求:

  1. 使用结构体数组来存储多个学生的信息。
  2. 程序应具备清晰的菜单界面,用户可以通过输入数字选择不同的功能。
  3. 各个功能模块应封装成独立的函数,实现模块化设计。
  4. 对文件操作进行错误处理(如文件打开失败等)。
  5. 程序运行界面友好,提示信息清晰。

实验原理

本实验主要基于C语言的以下核心知识点:

  1. 结构体:为了存储每个学生的多种不同类型的数据(学号、姓名、多门成绩),使用 struct 关键字定义一个自定义的 Student 数据类型,将相关信息封装在一起。
  2. 数组:使用结构体数组 struct Student students[N] 来存储多个学生的记录,实现批量数据的处理。
  3. 函数:将每个功能(如 addStudent, displayStudents, searchStudent 等)编写成一个独立的函数,主函数 main 通过调用这些函数来组织整个程序的流程,体现了“分而治之”的模块化思想。
  4. 指针:在函数间传递结构体数组时,使用指针可以避免整个数组的拷贝,提高效率。void displayStudents(struct Student *stu, int count)
  5. 文件I/O:使用 fopen() 以二写模式("wb")打开文件,使用 fwrite() 将结构体数组一次性写入文件,使用 fopen() 以二读模式("rb")打开文件,使用 fread() 将文件内容一次性读入结构体数组,这种方式高效且能保持数据结构。
  6. 循环与选择结构:使用 whilefor 循环实现菜单的反复显示和选择,使用 switch-case 语句根据用户的选择执行不同的功能分支。

实验步骤与过程

  1. 需求分析与设计

    • 定义 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()
  2. 编码实现

    c语言程序设计 实验报告
    (图片来源网络,侵删)
    • 创建项目,编写 main 函数,包含主循环 while(1)switch-case 结构。
    • 依次实现各个功能函数。
    • addStudent 函数中,提示用户输入信息并赋值给结构体数组元素。
    • displayStudents 函数中,使用循环遍历数组,并用 printf 格式化输出。
    • searchStudent 函数中,遍历数组,比较学号,找到后返回索引。
    • modifyStudentdeleteStudent 函数中,先调用 searchStudent 找到目标索引,再进行修改或删除操作(删除操作通常是将后一个元素前移)。
    • saveToFile 函数中,以二进制写模式打开文件,检查文件指针是否为 NULL,然后使用 fwrite 将整个数组写入。
    • loadFromFile 函数中,以二进制读模式打开文件,使用 fread 读取数据到数组,并返回实际读取的学生数量。
  3. 编译与调试

    • 使用编译器(如VS/Dev-C++)编译源代码,检查并修复语法错误。
    • 运行程序,测试每个功能,录入数据后,选择“显示”功能看是否正确;录入后选择“保存”,再重新运行程序看是否自动“读取”到了数据。
    • 使用调试器(如VS的断点、监视窗口)跟踪变量值,定位逻辑错误(如查询不到数据、修改无效等)。
  4. 测试与优化

    • 进行边界测试,如删除最后一个学生、修改不存在的学号等。
    • 优化用户提示信息,使其更友好。
    • 检查内存泄漏等问题(本实验中主要关注文件和数组操作)。

核心代码与注释

#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:保存与读取功能 (展示执行“保存”后,退出程序再重新进入,数据依然存在)

结果分析: 从运行结果可以看出,本程序成功实现了所有要求的功能:

  1. 用户可以通过菜单选择进行各种操作,交互界面清晰。
  2. 录入的学生信息能够被正确地存储在结构体数组中。
  3. 显示功能能够以整齐的表格形式输出所有学生数据。
  4. 查询、修改、删除功能都能准确地根据学号找到目标学生并进行相应操作。
  5. 数据保存功能成功将内存中的学生信息写入了 students.dat 文件。
  6. 数据读取功能在程序重启时能够从文件中恢复之前的数据,实现了数据的持久化。

遇到的问题与解决方案:

  1. 问题:在 scanf 读取字符串后,getchar() 无法清除后续的换行符,导致菜单选择被跳过。 解决方案:在 scanf 后调用 getchar() 来吸收换行符,对于数字输入,scanf 本身会消耗换行符,但对于字符串输入,必须手动处理。
  2. 问题:删除学生记录时,直接将数组大小减一,但没有处理数组元素,导致数据错乱。 解决方案:采用元素前移的方式,将被删除元素之后的所有记录向前移动一位,然后将 student_count 减一。
  3. 问题:文件读写时,没有检查 fopen 的返回值,如果文件因权限问题无法打开,程序会崩溃。 解决方案:增加 if (fp == NULL) 的判断,并在文件打开失败时给出明确的错误提示,而不是直接进行读写操作。

实验总结与心得

通过本次实验,我系统地复习和巩固了C语言的核心知识点,包括结构体、数组、函数、指针和文件操作,我深刻体会到模块化编程的重要性,它将一个复杂的系统分解成一个个功能独立、易于理解和维护的小模块,大大降低了编程的难度。

在设计过程中,我学会了如何从需求出发,设计合理的数据结构(使用结构体)和算法(查找、删除、修改),在编码实现时,我更加注重代码的规范性和健壮性,例如增加了对文件操作失败的处理,以及对用户非法输入的提示。

最大的收获是理解了指针在函数间传递复杂数据时的高效性,以及文件I/O在实现数据持久化方面的关键作用,这次实验不仅提升了我的编程技能,更锻炼了我分析问题、设计解决方案和动手调试的综合能力,在未来的学习中,我将更加注重代码的优化和用户体验,力求写出更高质量、更完善的程序。

-- 展开阅读全文 --
头像
单片机C语言程序设计PDF如何高效学习?
« 上一篇 02-18
dede data目录转移
下一篇 » 02-18

相关文章

取消
微信二维码
支付宝二维码

目录[+]