目录
[二、product.c 函数详解](#二、product.c 函数详解)
[1. 初始化商品列表 Init_products](#1. 初始化商品列表 Init_products)
[2. 添加商品 add_product](#2. 添加商品 add_product)
[3. 显示商品 display_products](#3. 显示商品 display_products)
[4. 修改商品 mod_product](#4. 修改商品 mod_product)
[5.删除函数 del_product](#5.删除函数 del_product)
[6.查询函数 que_product](#6.查询函数 que_product)
[三、main.c 主函数详解](#三、main.c 主函数详解)
[1. 主函数 main](#1. 主函数 main)
[2. 辅助函数 clear_screen](#2. 辅助函数 clear_screen)
[四、fileio.c 文件详解](#四、fileio.c 文件详解)
[1. 保存数据到文件 save_to_file](#1. 保存数据到文件 save_to_file)
[2. 加载数据 load_from_file](#2. 加载数据 load_from_file)
[3. main.c 主函数协同工作](#3. main.c 主函数协同工作)
[1. 动态内存管理](#1. 动态内存管理)
[2. 结构体的使用](#2. 结构体的使用)
[3. 输入输出安全](#3. 输入输出安全)
[4. 文件操作](#4. 文件操作)
前言:
当前这篇博客是测试版,教大家相关添加单个商品,显示所有商品,修改单个商品知识点;
看之前建议先看上篇博客:
(C语言)超市管理系统(测试版)(指针)(数据结构)(二进制文件读写)-CSDN博客

共6个文件(加上二进制文件);
源代码:
product.h
cpp
//product.h
#pragma once //防止头文件重复定义
#define NAME_LEN 50 //商品名称最大容量
//单个商品结构体
typedef struct {
int id;//商品编号
char name[NAME_LEN];//商品名字
float price;//商品单价
int stock;//商品库存
}Product;
//商品列表表结构体
typedef struct {
Product* Data;//指向单个商品数组的指针
int count;//当前商品数量
}ProductList;
// 函数原型
void Init_products(ProductList* list);//初始化商品列表结构体
void add_product(ProductList* list,Product* product);//添加单个商品
void display_products(ProductList* list);//显示所有商品
void mod_product(ProductList* list, Product* product);//修改单个商品
product.c
cpp
//product.c
#include "product.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//初始化商品列表结构体
void Init_products(ProductList* list) {
list->Data = NULL;//指针置空,防止野指针
list->count = 0;//商品数量归0
}
//添加单个商品
void add_product(ProductList* list,Product* product) {
//1.扩展空间
Product* listnew_Data = realloc(list->Data, (list->count + 1) * sizeof(Product));
if (listnew_Data==NULL) {
printf("内存分配失败!\n");
exit(EXIT_FAILURE);
}
list->count++;
list->Data = listnew_Data;//依然用老数组表示描述
//2.ID自动生成
list->Data[list->count - 1].id = list->count;
printf("商品ID:%d\n",list->count);
//3.商品信息录入
printf("请输入商品名称:");
scanf("%49s", list->Data[list->count-1].name);
printf("请输入单价:");
scanf("%f", &list->Data[list->count-1].price);
printf("请输入库存:");
scanf("%d", &list->Data[list->count-1].stock);
printf("添加成功!\n");
}
//显示所有商品
void display_products(ProductList* list) {
//1.判断列表是否为空
if (list->count == 0) {
printf("库存为空\n");
return;
}
//2.打印表头
printf("\n%5s %-20s %10s %6s\n", "ID", "名称", "单价", "库存");
printf("--------------------------------------------\n");
//3.打印商品信息
for (int i = 0; i < list->count; i++) {
printf("%5d %-20s %10.2f %5d\n",
list->Data[i].id,
list->Data[i].name,
list->Data[i].price,
list->Data[i].stock);
}
}
//修改单个商品
void mod_product(ProductList* list, Product* product) {
//1.判断列表是否为空
if (list->count == 0) {
printf("库存为空\n");
return;
}
//2.输入要修改的ID
int id_0;
printf("请输入要修改的ID:");
scanf("%d", &id_0);
//3.判断ID是否存在
if (id_0 > list->count) {
printf("ID不存在!\n");
return;
}
//4.找要修改商品的ID
int i=0;
for (i; i < list->count; i++) {
if (id_0 == list->Data[i].id) {
break;
}
}
//5.修改商品
printf("\n%5s %-20s %10s %6s\n", "ID", "名称", "单价", "库存");
printf("--------------------------------------------\n");
printf("%5d %-20s %10.2f %5d\n",
list->Data[i].id,
list->Data[i].name,
list->Data[i].price,
list->Data[i].stock);
printf("--------------------------------------------\n");
printf("修改商品名称:");
scanf("%49s", list->Data[i].name);
printf("修改单价:");
scanf("%f", &list->Data[i].price);
printf("修改库存:");
scanf("%d", &list->Data[i].stock);
printf("修改成功!\n");
}
fileio.h
cpp
//fileio.h
#pragma once
#include "product.h"
// 文件操作函数原型
void save_to_file(const char* filename, const ProductList* list);
void load_from_file(const char* filename, ProductList* list);
fileio.c
cpp
//fileio.c
//引用头文件
#include <stdio.h>
#include <stdlib.h>
#include "product.h"
// 保存数据到文件(二进制写入)
void save_to_file(const char* filename, const ProductList* list) {
//1.打开文件(二进制写入模式)
FILE* fp = fopen(filename, "wb");
// "wb":二进制写入模式,会清空原文件内容
// 若文件不存在则创建新文件
if (!fp) { // fp == NULL 表示打开失败
perror("保存失败"); // 输出错误信息(包含具体原因,如权限不足)
exit(EXIT_FAILURE); // 终止程序,EXIT_FAILURE 表示异常退出
}
//2.先写入商品数量(int 类型)
fwrite(&list->count,sizeof(int),1,fp);
// &list->count:取商品数量的内存地址
// sizeof(int):每个元素的大小(4字节)
// 1:写入1个元素
// fp:文件指针
//3.再写入所有商品数据(Product 结构体数组)
fwrite(list->Data, sizeof(Product), list->count, fp);
// list->Data:商品数组首地址
// sizeof(Product):每个商品占用的字节数
// list->count:要写入的商品数量
//4.关闭文件
fclose(fp);
}
// 从文件加载数据(二进制读取)
void load_from_file(const char* filename, ProductList* list) {
//1.初始化结构体(防御性编程)
Init_products(&list);//初始化商品列表结构体
//2.尝试打开文件(二进制读取模式)
FILE* fp = fopen(filename, "rb");// "rb":二进制读取模式,文件不存在时返回 NULL
if (!fp) {//文件打开失败处理
return; // 保持 list 的初始状态(count=0, Data=NULL)
}
//3.读取商品数量(int 类型)
fread(&list->count,sizeof(int),1,fp);
// 从文件中读取4字节到 list->count
//4.根据数量分配内存
list->Data = malloc(list->count * sizeof(Product));
// 计算总字节数 = 商品数量 × 单个商品大小
//检查是否分配成功
if (list->Data == NULL) { // list->Data == NULL 表示失败
printf("内存分配失败\n");
exit(EXIT_FAILURE); // 终止程序
}
//5.读取所有商品数据
fread(list->Data, sizeof(Product), list->count, fp);
// 将文件内容直接读入 Data 数组
//6.关闭文件
fclose(fp);
}
main.c
cpp
//mian.c
#include <stdio.h>
#include <stdlib.h>
#include "product.h"
#include "fileio.h"
#define FILENAME "products.dat"//宏定义文件名
//清屏操作
void clear_screen() {
//判断是否为Windows系统
#ifdef _WIN32
system("cls");
//其他系统
#else
system("clear");
#endif
}
// 显示主菜单(用户界面)
void display_menu() {
printf("\n超市管理系统\n");
printf("1. 添加商品\n");
printf("2. 显示所有商品\n");
printf("3. 修改商品信息\n");
printf("4. 删除商品\n");
printf("5. 搜索商品\n");
printf("6. 保存并退出\n");
printf("请选择操作:");
}
int main() {
//1.创建结构体并初始化
Product product;//创建单个商品结构体
ProductList list;//创建商品列表结构体
Init_products(&list);//初始化
//2.读文件
load_from_file(FILENAME, &list);//读文件
//3.选择模块
int choice;//选择选项
while (1) {
display_menu();//显示菜单
scanf("%d", &choice);//输入选项
switch (choice) {
case 1:
clear_screen();
add_product(&list,&product);
printf("--------------------------------------------\n");
break;
case 2:
clear_screen();
display_products(&list);
printf("--------------------------------------------\n");
break;
case 3:
clear_screen();
mod_product(&list,&product);
printf("--------------------------------------------\n");
break;
case 6:
save_to_file(FILENAME, &list); // 保存数据
free(list.Data); // 释放动态内存
printf("系统已退出\n");
return 0; // 正确退出
default:
printf("无效输入\n");
}
}
}
代码解析:
一、程序结构概述
整个程序分为三个核心模块:
数据管理模块 (
product.c
):处理商品的增删改查文件操作模块 (
fileio.c
):负责数据保存与加载主控模块 (
main.c
):协调程序流程和用户交互
二、product.c 函数详解
1. 初始化商品列表
Init_products
cppvoid Init_products(ProductList* list) { list->Data = NULL; // 指针置空,防止野指针 list->count = 0; // 商品数量归0 }
功能:
- 初始化商品列表结构体,确保程序启动时处于干净状态。
实现步骤:
Data = NULL
:将动态数组指针置空,避免指向随机内存。
count = 0
:商品数量初始化为0。为什么这样写:
防御性编程:确保程序启动时没有残留数据。
动态内存安全 :
Data
初始为NULL
,realloc
在首次调用时会自动分配内存。如何使用:
cppProductList list; // 声明一个商品列表 Init_products(&list); // 初始化列表(必须调用)
2. 添加商品
add_product
cppvoid add_product(ProductList* list, Product* product) { // 1. 扩展内存 Product* listnew_Data = realloc(list->Data, (list->count + 1) * sizeof(Product)); if (listnew_Data == NULL) { printf("内存分配失败!\n"); exit(EXIT_FAILURE); } list->count++; list->Data = listnew_Data; // 2. 自动生成ID list->Data[list->count - 1].id = list->count; printf("商品ID:%d\n", list->count); // 3. 录入商品信息 printf("请输入商品名称:"); scanf("%49s", list->Data[list->count-1].name); printf("请输入单价:"); scanf("%f", &list->Data[list->count-1].price); printf("请输入库存:"); scanf("%d", &list->Data[list->count-1].stock); printf("添加成功!\n"); }
功能:
- 动态扩展内存,添加新商品并自动生成ID。
实现步骤:
内存扩展 :使用
realloc
将数组大小增加1个商品位置。错误处理:检查内存是否分配成功,失败则终止程序。
生成ID:新商品ID = 当前商品总数 + 1(例如第一个商品ID=1)。
输入信息:依次输入名称、单价、库存。
为什么这样写:
动态内存管理 :
realloc
自动处理内存扩展,无需手动复制数据。简单ID生成 :直接使用
count
作为ID,但存在删除商品后ID不连续的问题(后续改进点)。如何使用:
cppProductList list; Init_products(&list); add_product(&list, NULL); // 添加第一个商品
输入示例:
cpp请输入商品名称:苹果 请输入单价:5.5 请输入库存:20
注意事项:
输入缓冲区问题 :连续使用
scanf
可能导致残留换行符,需清空缓冲区(代码未处理)。名称输入限制 :
%49s
防止溢出,但无法输入带空格的名称(如"红富士苹果")。
3. 显示商品
display_products
cppvoid display_products(ProductList* list) { if (list->count == 0) { printf("库存为空\n"); return; } printf("\n%5s %-20s %10s %6s\n", "ID", "名称", "单价", "库存"); printf("--------------------------------------------\n"); for (int i = 0; i < list->count; i++) { printf("%5d %-20s %10.2f %5d\n", list->Data[i].id, list->Data[i].name, list->Data[i].price, list->Data[i].stock); } }
功能:
- 以表格形式打印所有商品信息,处理空列表情况。
实现步骤:
空列表检查:直接返回提示信息。
打印表头:使用格式化字符串对齐标题。
遍历打印:循环输出每个商品的字段。
为什么这样写:
用户体验:清晰的表格布局提升可读性。
格式控制符:
%5d
:ID占5字符宽度,右对齐。
%-20s
:名称左对齐,占20字符。
%10.2f
:单价保留两位小数,总宽度10。如何使用:
cppdisplay_products(&list); // 显示当前所有商品
输出示例:
cppID 名称 单价 库存 -------------------------------------------- 1 苹果 5.50 20 2 香蕉 3.80 15
4. 修改商品
mod_product
cppvoid mod_product(ProductList* list, Product* product) { if (list->count == 0) { printf("库存为空\n"); return; } int id_0; printf("请输入要修改的ID:"); scanf("%d", &id_0); if (id_0 > list->count) { printf("ID不存在!\n"); return; } int i=0; for (i; i < list->count; i++) { if (id_0 == list->Data[i].id) { break; } } // 显示原信息并修改 printf("\n%5s %-20s %10s %6s\n", "ID", "名称", "单价", "库存"); printf("--------------------------------------------\n"); printf("%5d %-20s %10.2f %5d\n", list->Data[i].id, list->Data[i].name, list->Data[i].price, list->Data[i].stock); printf("--------------------------------------------\n"); printf("修改商品名称:"); scanf("%49s", list->Data[i].name); printf("修改单价:"); scanf("%f", &list->Data[i].price); printf("修改库存:"); scanf("%d", &list->Data[i].stock); printf("修改成功!\n"); }
功能:
- 根据用户输入的ID查找商品,修改其信息。
实现步骤:
空列表检查:直接返回提示。
输入目标ID:用户指定要修改的商品。
ID存在性检查 :错误判断逻辑不严谨(
id_0 > count
可能漏判)。遍历查找:找到对应商品的数组索引。
显示并修改:打印原信息,逐项修改。
为什么这样写:
直观交互:先展示原信息再修改,减少误操作。
直接修改内存:通过指针直接修改数组元素。
如何使用:
cppmod_product(&list, NULL); // 修改ID为2的商品
输入示例:
cpp请输入要修改的ID:2 ...(显示原信息)... 修改商品名称:香蕉 修改单价:4.5 修改库存:25
5.删除函数
del_product
cpp//删除单个商品 void del_product(ProductList* list) { //1.显示所有商品 display_products(list); printf("--------------------------------------------\n"); //2.输入要删除的ID int id_0; printf("请输入要删除的ID:"); scanf("%d", &id_0); //3.判断ID是否存在 if (id_0 > list->count) { printf("ID不存在!\n"); return; } //4.找要删除商品的ID int i = 0; for (i; i < list->count; i++) {//此时的i+1就是当前商品ID if (id_0 == list->Data[i].id) { break; } } //5.删除商品 for (int j = i; j < list->count - 1; j++) { list->Data[j] = list->Data[j + 1]; } printf("删除成功!\n"); list->count--;//商品数量减一 //6.重新生成商品ID if (list->count == 1) { list->Data[0].id = 1; } else { list->Data[list->count - 1].id = list->Data[list->count - 2].id + 1; } }
功能
根据用户输入的ID删除指定商品,并调整商品列表以保持数据连续性,最后重新生成所有商品的ID以确保ID连续。
实现步骤
显示所有商品
- 调用
display_products
显示当前所有商品信息,供用户参考。输入要删除的ID
- 用户输入目标商品的ID。
判断ID是否存在
- 检查输入的ID是否超过当前商品总数(
id_0 > list->count
),若超过则提示不存在。查找目标商品的索引
- 遍历商品列表,找到与输入ID匹配的商品索引
i
。删除商品并调整数组
- 将索引
i
之后的商品依次前移一位,覆盖目标商品。更新商品数量
- 减少
list->count
以反映删除后的商品总数。重新生成所有商品的ID
- 若删除后仅剩一个商品,将其ID设为1;否则,将最后一个商品的ID设为前一个ID加1。
为什么这样写
显示商品列表:帮助用户确认要删除的商品ID。
简单ID存在性检查 :假设商品ID是连续递增的(ID = 1, 2, 3...),通过比较输入ID与
list->count
快速判断是否存在。数组前移覆盖:通过循环将后续元素前移,逻辑简单但效率较低(时间复杂度为O(n))。
强制ID连续:删除后重新生成所有ID,确保ID连续,避免出现空缺(如删除ID=2后,原ID=3变为ID=2)。
如何使用
cppdel_product(&list); // 删除ID为2的商品
输入示例
cpp(显示所有商品) -------------------------------------------- 请输入要删除的ID:2 删除成功!
6.查询函数
que_product
cpp//查询单个商品 void que_product(ProductList* list) { //1.判断列表是否为空 if (list->count == 0) { printf("库存为空\n"); return; } //2.输入要搜索的ID int id_0; printf("请输入要搜索的ID:"); scanf("%d", &id_0); //3.判断ID是否存在 if (id_0 > list->count) { printf("ID不存在!\n"); return; } //4.找要搜索商品的ID int i = 0; for (i; i < list->count; i++) { if (id_0 == list->Data[i].id) {//此时的i+1就是当前商品ID break; } } //5.显示商品 printf("搜索成功!\n"); printf("\n%5s %-20s %10s %6s\n", "ID", "名称", "单价", "库存"); printf("--------------------------------------------\n"); printf("%5d %-20s %10.2f %5d\n", list->Data[i].id, list->Data[i].name, list->Data[i].price, list->Data[i].stock); }
功能
根据用户输入的ID查找并显示指定商品的详细信息。
实现步骤
判断列表是否为空
- 若商品数量为0,直接提示库存为空。
输入要查询的ID
- 用户输入目标商品的ID。
判断ID是否存在
- 检查输入的ID是否超过当前商品总数(
id_0 > list->count
),若超过则提示不存在。查找目标商品的索引
- 遍历商品列表,找到与输入ID匹配的商品索引
i
。显示商品信息
- 以表格形式输出该商品的ID、名称、单价和库存。
为什么这样写
快速存在性检查 :假设ID连续,通过比较输入ID与
list->count
快速过滤无效ID。直接遍历查找:线性搜索整个数组,逻辑简单但效率较低(时间复杂度为O(n))。
格式化输出 :保持与
display_products
一致的表格布局,提升用户体验。
如何使用
cppque_product(&list); // 查询ID为3的商品
输入示例
cpp请输入要搜索的ID:3 搜索成功! ID 名称 单价 库存 -------------------------------------------- 3 面包 5.50 30
三、main.c 主函数详解
1. 主函数
main
cppint main() { Product product; // 单个商品(未实际使用) ProductList list; // 商品列表 Init_products(&list); // 初始化列表 load_from_file(FILENAME, &list); // 加载数据 int choice; while (1) { display_menu(); // 显示菜单 scanf("%d", &choice); switch (choice) { case 1: add_product(&list, &product); break; case 2: display_products(&list); break; case 3: mod_product(&list, &product); break; case 6: save_to_file(FILENAME, &list); // 保存数据 free(list.Data); // 释放内存 printf("系统已退出\n"); return 0; default: printf("无效输入\n"); } } }
功能:
- 程序入口,管理整个生命周期:初始化→加载数据→循环处理用户操作→退出保存。
实现步骤:
初始化:创建商品列表并初始化。
加载数据:从文件读取历史数据。
主循环:
显示菜单,获取用户选择。
调用对应功能函数。
退出处理:保存数据并释放内存。
关键设计:
循环结构 :
while(1)
保持程序持续运行。内存释放 :退出前必须
free(list.Data)
,否则内存泄漏。模块化调用 :通过
switch-case
调用各功能函数。用户交互流程:
cppgraph TD A[启动程序] --> B[加载数据] B --> C{显示菜单} C --> D[用户选择] D -->|1-5| E[执行操作] E --> C D -->|6| F[保存并退出]
2. 辅助函数
clear_screen
cppvoid clear_screen() { #ifdef _WIN32 system("cls"); // Windows清屏 #else system("clear"); // Linux/Mac清屏 #endif }
功能:
- 清空控制台屏幕,提升界面整洁度。
为什么这样写:
跨平台兼容:通过预编译指令区分系统。
简单调用 :
system
函数直接执行系统命令。如何使用:
cppclear_screen(); // 清空屏幕后显示新内容
四、fileio.c 文件详解
1. 保存数据到文件
save_to_file
cppvoid save_to_file(const char* filename, const ProductList* list) { // 1. 打开文件(二进制写入模式) FILE* fp = fopen(filename, "wb"); if (!fp) { perror("保存失败"); exit(EXIT_FAILURE); } // 2. 写入商品数量 fwrite(&list->count, sizeof(int), 1, fp); // 3. 写入所有商品数据 fwrite(list->Data, sizeof(Product), list->count, fp); // 4. 关闭文件 fclose(fp); }
功能 :
将商品列表数据保存到二进制文件中,确保程序退出后数据不丢失。
逐行解析:
打开文件:
"wb"
:二进制写入模式,清空原文件内容。若文件不存在则新建。
fopen
失败时,perror
输出具体错误(如权限不足),exit
终止程序。写入商品数量:
fwrite(&list->count, ...)
:将商品数量(int
类型)写入文件开头。作用:后续读取时,根据此值分配内存。
写入商品数组:
fwrite(list->Data, ...)
:将整个商品数组写入文件。二进制优势:直接写入内存数据,无需格式转换,高效且保留浮点精度。
关闭文件:
fclose
:确保数据从缓冲区写入磁盘。关键知识点:
二进制文件格式 :
文件内容为原始内存数据,不可直接阅读,但读写速度快。
数据持久化:程序退出后,数据通过文件保存,下次启动可恢复。
使用示例:
cppProductList list; // ...添加商品... save_to_file("data.dat", &list); // 保存数据
注意事项:
跨平台问题:不同系统可能结构体内存对齐不同,导致文件不兼容。
文件损坏风险:若写入过程被中断(如程序崩溃),文件可能损坏。
2. 加载数据
load_from_file
cppvoid load_from_file(const char* filename, ProductList* list) { // 1. 初始化结构体 Init_products(list); // 2. 打开文件(二进制读取模式) FILE* fp = fopen(filename, "rb"); if (!fp) return; // 3. 读取商品数量 fread(&list->count, sizeof(int), 1, fp); // 4. 分配内存 list->Data = malloc(list->count * sizeof(Product)); if (!list->Data) { printf("内存分配失败\n"); exit(EXIT_FAILURE); } // 5. 读取商品数据 fread(list->Data, sizeof(Product), list->count, fp); // 6. 关闭文件 fclose(fp); }
功能 :
从二进制文件加载商品数据到内存,恢复程序上次运行状态。
逐行解析:
初始化列表 :
Init_products
清空现有数据,防止残留值干扰。打开文件 :
"rb"
:二进制读取模式,文件不存在时返回NULL
,跳过加载。读取数量 :
fread(&list->count, ...)
:从文件开头读取商品数量。分配内存 :
malloc
根据商品数量分配足够内存,失败时终止程序。读取数据 :
fread
将文件中的商品数据直接读入Data
数组。关闭文件 :
释放文件资源。
关键知识点:
防御性编程:加载前初始化列表,避免脏数据。
内存管理 :动态分配的内存需在退出时通过
free
释放。使用示例:
cppProductList list; load_from_file("data.dat", &list); // 加载数据
注意事项:
文件验证缺失:若文件被篡改(如数量与实际数据不符),程序会崩溃。
字节序问题:跨平台时需处理大小端差异(如从Windows写,Linux读)。
3. main.c 主函数协同工作
1. 主函数代码片段
cppint main() { ProductList list; Init_products(&list); load_from_file(FILENAME, &list); // 启动时加载数据 while (1) { // ...菜单处理... switch (choice) { case 6: save_to_file(FILENAME, &list); // 退出前保存 free(list.Data); // 释放内存 return 0; } } }
2. 数据生命周期管理
启动流程:
Init_products
:初始化空列表。
load_from_file
:尝试加载数据,文件不存在则保持空列表。运行期间:
- 用户通过菜单操作增删改查,所有变动仅在内存中。
退出流程:
save_to_file
:将内存数据保存到文件。
free(list.Data)
:释放动态数组内存,防止泄漏。3. 关键设计思想
数据持久化:通过文件实现"记忆功能",关闭程序不丢数据。
资源管理:
加载时分配内存,退出时释放,遵循"谁分配谁释放"原则。
文件操作封装为独立函数,提高代码可维护性。
4.联合调试示例
1. 正常流程验证
第一次运行:
cpp添加商品:名称=苹果,单价=5.5,库存=20 保存退出 → 生成data.dat文件
文件内容:
前4字节:
01 00 00 00
(数量1)后续内容:
01 00 00 00
(ID=1) + 名称、单价、库存的二进制数据。第二次运行:
自动加载文件,显示已有商品。
修改库存为30后保存退出。
2. 异常场景处理
文件被删除:启动时加载失败,列表为空。
文件损坏:若手动修改文件导致数据错乱,程序可能崩溃。
五、核心知识点总结
1. 动态内存管理
realloc
的作用 :动态调整内存大小,首次调用时等效于malloc
。错误处理 :必须检查返回值是否为
NULL
。内存释放 :
free
必须与malloc/realloc
配对使用。
2. 结构体的使用
数据封装 :将商品信息打包为
Product
结构体。列表管理 :
ProductList
封装动态数组和长度,提升代码可维护性。
3. 输入输出安全
缓冲区溢出防护 :
scanf("%49s")
限制输入长度。格式化输出 :
printf
的格式控制符对齐数据。
4. 文件操作
二进制模式 :
"wb"
和"rb"
确保数据精确存储。数据序列化:直接读写结构体内存,高效但需注意平台兼容性。
相关运行截图:
注:该代码是本人自己所写,可能不够好,不够简便,欢迎大家指出我的不足之处。如果遇见看不懂的地方,可以在评论区打出来,进行讨论,或者联系我。上述内容全是我自己理解的,如果你有别的想法,或者认为我的理解不对,欢迎指出!!!如果可以,可以点一个免费的赞支持一下吗?谢谢各位彦祖亦菲!!!!!