大家好,我是网域小星球。
前面我们已经学习了结构体、指针、动态内存、文件操作以及简易项目框架。这一篇我们把所有知识点彻底打通融合,写一个更完善、更灵活、可长期保存数据的小型综合程序。
本篇依然全程适配 VS2022,代码可直接复制运行,逻辑清晰、注释完整,适合作为课程设计、实验报告、博客实战案例。
目录
一、本章学习目标
- 学会用结构体指针管理一批学生数据
- 使用动态内存实现可自动扩容的学生列表
- 结合文件操作实现数据持久化(重启程序不丢失)
- 完善菜单功能:展示、添加、查找、修改、保存、退出
- 掌握大型综合程序的编写思路与规范
- 学会常见 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);
}
四、本章重点知识点总结
-
动态内存扩容 使用
realloc自动扩容,不再受固定数组大小限制。 -
结构体指针访问 直接用
stus[i]即可,等价于指针偏移访问。 -
数据持久化保存 / 加载文件,关闭程序再打开数据依然存在。
-
程序健壮性
- 容量自动检查
- 文件判空
- 退出时
free释放内存
-
模块化设计一个功能一个函数,结构清晰、易于扩展。
五、高频易错点
- 忘记
free导致内存泄漏 realloc不判空直接使用- 文件路径错误或权限不足
- 加载文件时覆盖原有数据
- 学号重复未做判断
- 字符串直接赋值导致报错(必须用 strcpy /snprintf)
六、本章核心总结
- 结构体 + 指针 + 动态内存 = 灵活高效的数据管理
- 文件操作实现真正意义上的 "持久化存储"
- 菜单 + switch 是控制台小项目通用框架
- 模块化函数让代码更易读、易维护
- 本篇可直接作为课程设计、大作业、实验报告使用
下期预告
下一篇我们系统学习字符串核心操作函数,理解底层原理并自己手写实现,为笔试面试打下扎实基础。