C 语言从 0 入门(十七)|结构体指针 + 动态内存 + 文件综合实战

大家好,我是网域小星球。

前面我们已经学习了结构体、指针、动态内存、文件操作以及简易项目框架。这一篇我们把所有知识点彻底打通融合,写一个更完善、更灵活、可长期保存数据的小型综合程序。

本篇依然全程适配 VS2022,代码可直接复制运行,逻辑清晰、注释完整,适合作为课程设计、实验报告、博客实战案例。

目录

一、本章学习目标

二、功能设计(菜单)

三、完整可运行代码

四、本章重点知识点总结

五、高频易错点

六、本章核心总结

下期预告

一、本章学习目标

  1. 学会用结构体指针管理一批学生数据
  2. 使用动态内存实现可自动扩容的学生列表
  3. 结合文件操作实现数据持久化(重启程序不丢失)
  4. 完善菜单功能:展示、添加、查找、修改、保存、退出
  5. 掌握大型综合程序的编写思路与规范
  6. 学会常见 bug 排查与健壮性处理

二、功能设计(菜单)

cpp 复制代码
=========================
    学生综合管理系统
=========================
 1 展示所有学生
 2 添加学生信息
 3 按学号查找学生
 4 修改学生成绩
 5 保存数据到文件
 6 从文件加载数据
 0 退出系统
=========================

三、完整可运行代码

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>

// 学生结构体
struct Student {
    int id;
    char name[20];
    float score;
};

// 动态管理全局变量
struct Student* stus = NULL;
int count = 0;      // 当前人数
int capacity = 0;   // 总容量

// 函数声明
void showMenu();
void checkCapacity();
void showAll();
void addStu();
void findStu();
void modifyScore();
void saveToFile();
void loadFromFile();

int main() {
    int choice;
    while (1) {
        showMenu();
        printf("请输入选择:");
        scanf("%d", &choice);

        switch (choice) {
        case 1: showAll();            break;
        case 2: addStu();             break;
        case 3: findStu();            break;
        case 4: modifyScore();        break;
        case 5: saveToFile();         break;
        case 6: loadFromFile();       break;
        case 0:
            printf("感谢使用,再见!\n");
            free(stus);
            stus = NULL;
            return 0;
        default:
            printf("输入错误,请重新选择!\n");
        }
        printf("\n");
    }
}

// 显示菜单
void showMenu() {
    printf("=========================\n");
    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");
}

// 检查容量,满了就扩容
void checkCapacity() {
    if (count < capacity) return;

    int newCap = (capacity == 0) ? 5 : capacity * 2;
    struct Student* temp = (struct Student*)realloc(stus, newCap * sizeof(struct Student));

    if (temp == NULL) {
        printf("内存扩容失败!\n");
        return;
    }
    stus = temp;
    capacity = newCap;
}

// 展示所有
void showAll() {
    printf("当前学生数:%d\n", count);
    if (count == 0) {
        printf("暂无学生数据!\n");
        return;
    }
    printf("学号\t姓名\t成绩\n");
    for (int i = 0; i < count; i++) {
        printf("%d\t%s\t%.1f\n", stus[i].id, stus[i].name, stus[i].score);
    }
}

// 添加学生
void addStu() {
    checkCapacity();
    printf("请输入 学号 姓名 成绩:");
    scanf("%d%s%f", &stus[count].id, stus[count].name, &stus[count].score);
    count++;
    printf("添加成功!\n");
}

// 按学号查找
void findStu() {
    int findId;
    printf("请输入要查找的学号:");
    scanf("%d", &findId);

    for (int i = 0; i < count; i++) {
        if (stus[i].id == findId) {
            printf("找到:%d %s %.1f\n", stus[i].id, stus[i].name, stus[i].score);
            return;
        }
    }
    printf("未找到该学生!\n");
}

// 修改成绩
void modifyScore() {
    int id;
    printf("请输入要修改的学号:");
    scanf("%d", &id);

    for (int i = 0; i < count; i++) {
        if (stus[i].id == id) {
            printf("当前成绩:%.1f\n", stus[i].score);
            printf("请输入新成绩:");
            scanf("%f", &stus[i].score);
            printf("修改成功!\n");
            return;
        }
    }
    printf("未找到该学生!\n");
}

// 保存到文件
void saveToFile() {
    FILE* fp = fopen("stu_data.txt", "w");
    if (fp == NULL) {
        printf("文件打开失败!\n");
        return;
    }
    for (int i = 0; i < count; i++) {
        fprintf(fp, "%d %s %.1f\n", stus[i].id, stus[i].name, stus[i].score);
    }
    fclose(fp);
    printf("已保存到 stu_data.txt\n");
}

// 从文件加载
void loadFromFile() {
    FILE* fp = fopen("stu_data.txt", "r");
    if (fp == NULL) {
        printf("文件不存在!\n");
        return;
    }

    count = 0;
    int id;
    char name[20];
    float score;

    while (fscanf(fp, "%d%s%f", &id, name, &score) != EOF) {
        checkCapacity();
        stus[count].id = id;
        snprintf(stus[count].name, sizeof(stus[count].name), "%s", name);
        stus[count].score = score;
        count++;
    }
    fclose(fp);
    printf("加载成功!共 %d 条数据\n", count);
}

四、本章重点知识点总结

  1. 动态内存扩容 使用 realloc 自动扩容,不再受固定数组大小限制。

  2. 结构体指针访问 直接用 stus[i] 即可,等价于指针偏移访问。

  3. 数据持久化保存 / 加载文件,关闭程序再打开数据依然存在。

  4. 程序健壮性

    • 容量自动检查
    • 文件判空
    • 退出时 free 释放内存
  5. 模块化设计一个功能一个函数,结构清晰、易于扩展。


五、高频易错点

  1. 忘记 free 导致内存泄漏
  2. realloc 不判空直接使用
  3. 文件路径错误或权限不足
  4. 加载文件时覆盖原有数据
  5. 学号重复未做判断
  6. 字符串直接赋值导致报错(必须用 strcpy /snprintf)

六、本章核心总结

  1. 结构体 + 指针 + 动态内存 = 灵活高效的数据管理
  2. 文件操作实现真正意义上的 "持久化存储"
  3. 菜单 + switch 是控制台小项目通用框架
  4. 模块化函数让代码更易读、易维护
  5. 本篇可直接作为课程设计、大作业、实验报告使用

下期预告

下一篇我们系统学习字符串核心操作函数,理解底层原理并自己手写实现,为笔试面试打下扎实基础。

相关推荐
傻啦嘿哟3 小时前
某招聘平台爬虫:爬取招聘岗位数据,分析各城市薪资水平
开发语言·爬虫·python
2501_933670793 小时前
2026秋招量化分析岗技能栈:Python、SQL、统计建模、回测项目怎么准备
开发语言·python·sql
李少兄4 小时前
JavaScript 数据类型完全指南
开发语言·javascript·ecmascript
Seoyoneh5 小时前
Agentic Workflow编排架构:云客服从“被动响应”迈向“主动执行”的技术实现
java·开发语言·架构
Tangyuewei5 小时前
Java 写 Agent:模型只是组件
java·开发语言
小玮看世界6 小时前
[Python]从合并区间到传感器融合区:合并区间在传感器区域融合的实际落地
开发语言·python
繁星蓝雨6 小时前
C++的设计与演进大纲(如何从C语言一步步成长为C++)
c语言·开发语言·c++·c++历史·c++演进
Evand J7 小时前
【MATLAB例程,图像降噪滤波9】自适应维纳滑动窗口滤波(Wiener)图像降噪、方法对比,有中文注释
开发语言·图像处理·计算机视觉·matlab·滑动窗口·滤波·降噪
yutian06067 小时前
C: Unix UTC 时间戳转为 UTC+8 北京时间
c语言·unix
杜大哥7 小时前
python程序:如何查看电脑【电池电量的剩余百分比】 和 【是否插入连接着充电器】?
开发语言·python