当前位置: 首页 > news >正文

(C语言)超市管理系统(测试2版)(指针)(数据结构)(清屏操作)

目录

前言:

源代码:

product.h

 product.c

 fileio.h

 fileio.c

 main.c

代码解析:

一、程序结构概述

二、product.c 函数详解

1. 初始化商品列表 Init_products

2. 添加商品 add_product

3. 显示商品 display_products

4. 修改商品 mod_product

三、main.c 主函数详解

1. 主函数 main

2. 辅助函数 clear_screen

四、核心知识点总结

1. 动态内存管理

2. 结构体的使用

3. 输入输出安全

4. 文件操作

五、动手实践建议


前言:

当前这篇博客是测试版,教大家相关添加单个商品,显示所有商品,修改单个商品知识点;

看之前建议先看上篇博客:

(C语言)超市管理系统(测试版)(指针)(数据结构)(二进制文件读写)-CSDN博客

共6个文件(加上二进制文件);

源代码:

product.h

//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

//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.输入要修改的IDint id_0;printf("请输入要修改的ID:");scanf("%d", &id_0);//3.判断ID是否存在if (id_0 > list->count) {printf("ID不存在!\n");return;}//4.找要修改商品的IDint 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

//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

//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":二进制读取模式,文件不存在时返回 NULLif (!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

//mian.c#include <stdio.h>
#include <stdlib.h>
#include "product.h"
#include "fileio.h"#define FILENAME "products.dat"//宏定义文件名//清屏操作
void clear_screen() {//判断是否为Windows系统
#ifdef _WIN32system("cls");//其他系统
#elsesystem("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");}}
}

代码解析:

一、程序结构概述

整个程序分为三个核心模块:

  1. 数据管理模块 (product.c):处理商品的增删改查

  2. 文件操作模块 (fileio.c):负责数据保存与加载

  3. 主控模块 (main.c):协调程序流程和用户交互


二、product.c 函数详解


1. 初始化商品列表 Init_products
void Init_products(ProductList* list) {list->Data = NULL;  // 指针置空,防止野指针list->count = 0;    // 商品数量归0
}

功能

  • 初始化商品列表结构体,确保程序启动时处于干净状态。

实现步骤

  1. Data = NULL:将动态数组指针置空,避免指向随机内存。

  2. count = 0:商品数量初始化为0。

为什么这样写

  • 防御性编程:确保程序启动时没有残留数据。

  • 动态内存安全Data 初始为 NULLrealloc 在首次调用时会自动分配内存。

如何使用

ProductList list;      // 声明一个商品列表
Init_products(&list);  // 初始化列表(必须调用)

2. 添加商品 add_product
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. 自动生成IDlist->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。

实现步骤

  1. 内存扩展:使用 realloc 将数组大小增加1个商品位置。

  2. 错误处理:检查内存是否分配成功,失败则终止程序。

  3. 生成ID:新商品ID = 当前商品总数 + 1(例如第一个商品ID=1)。

  4. 输入信息:依次输入名称、单价、库存。

为什么这样写

  • 动态内存管理realloc 自动处理内存扩展,无需手动复制数据。

  • 简单ID生成:直接使用 count 作为ID,但存在删除商品后ID不连续的问题(后续改进点)。

如何使用

ProductList list;
Init_products(&list);
add_product(&list, NULL);  // 添加第一个商品

输入示例

请输入商品名称:苹果
请输入单价:5.5
请输入库存:20

注意事项

  • 输入缓冲区问题:连续使用 scanf 可能导致残留换行符,需清空缓冲区(代码未处理)。

  • 名称输入限制%49s 防止溢出,但无法输入带空格的名称(如“红富士苹果”)。


3. 显示商品 display_products
void 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);}
}

功能

  • 以表格形式打印所有商品信息,处理空列表情况。

实现步骤

  1. 空列表检查:直接返回提示信息。

  2. 打印表头:使用格式化字符串对齐标题。

  3. 遍历打印:循环输出每个商品的字段。

为什么这样写

  • 用户体验:清晰的表格布局提升可读性。

  • 格式控制符

    • %5d:ID占5字符宽度,右对齐。

    • %-20s:名称左对齐,占20字符。

    • %10.2f:单价保留两位小数,总宽度10。

如何使用

display_products(&list);  // 显示当前所有商品

输出示例

   ID 名称                 单价    库存
--------------------------------------------1 苹果               5.50    202 香蕉               3.80    15

4. 修改商品 mod_product
void 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查找商品,修改其信息。

实现步骤

  1. 空列表检查:直接返回提示。

  2. 输入目标ID:用户指定要修改的商品。

  3. ID存在性检查:错误判断逻辑不严谨(id_0 > count 可能漏判)。

  4. 遍历查找:找到对应商品的数组索引。

  5. 显示并修改:打印原信息,逐项修改。

为什么这样写

  • 直观交互:先展示原信息再修改,减少误操作。

  • 直接修改内存:通过指针直接修改数组元素。

问题与改进

  • ID检查缺陷id_0 > list->count 假设ID连续且等于count,实际可能因删除操作导致ID大于count。

  • 未处理未找到ID:循环结束后未检查是否找到有效索引,可能导致越界访问。

如何使用

mod_product(&list, NULL);  // 修改ID为2的商品

输入示例

请输入要修改的ID:2
...(显示原信息)...
修改商品名称:香蕉
修改单价:4.5
修改库存:25

三、main.c 主函数详解


1. 主函数 main
int 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");}}
}

功能

  • 程序入口,管理整个生命周期:初始化→加载数据→循环处理用户操作→退出保存。

实现步骤

  1. 初始化:创建商品列表并初始化。

  2. 加载数据:从文件读取历史数据。

  3. 主循环

    • 显示菜单,获取用户选择。

    • 调用对应功能函数。

  4. 退出处理:保存数据并释放内存。

关键设计

  • 循环结构while(1) 保持程序持续运行。

  • 内存释放:退出前必须 free(list.Data),否则内存泄漏。

  • 模块化调用:通过 switch-case 调用各功能函数。

用户交互流程

graph TD
A[启动程序] --> B[加载数据]
B --> C{显示菜单}
C --> D[用户选择]
D -->|1-5| E[执行操作]
E --> C
D -->|6| F[保存并退出]

2. 辅助函数 clear_screen
void clear_screen() {
#ifdef _WIN32system("cls");   // Windows清屏
#elsesystem("clear"); // Linux/Mac清屏
#endif
}

功能

  • 清空控制台屏幕,提升界面整洁度。

为什么这样写

  • 跨平台兼容:通过预编译指令区分系统。

  • 简单调用system 函数直接执行系统命令。

如何使用

clear_screen();  // 清空屏幕后显示新内容

四、核心知识点总结


1. 动态内存管理
  • realloc 的作用:动态调整内存大小,首次调用时等效于 malloc

  • 错误处理:必须检查返回值是否为 NULL

  • 内存释放free 必须与 malloc/realloc 配对使用。


2. 结构体的使用
  • 数据封装:将商品信息打包为 Product 结构体。

  • 列表管理ProductList 封装动态数组和长度,提升代码可维护性。


3. 输入输出安全
  • 缓冲区溢出防护scanf("%49s") 限制输入长度。

  • 格式化输出printf 的格式控制符对齐数据。


4. 文件操作
  • 二进制模式"wb" 和 "rb" 确保数据精确存储。

  • 数据序列化:直接读写结构体内存,高效但需注意平台兼容性。


五、动手实践建议

  1. 添加删除功能:实现 delete_product 函数,练习内存缩减 (realloc)。

  2. 增强输入验证:检查价格是否为负数,库存是否为整数。

  3. 实现搜索功能:按名称或ID查找商品,练习字符串处理 (strstr)。

 注:该代码是本人自己所写,可能不够好,不够简便,欢迎大家指出我的不足之处。如果遇见看不懂的地方,可以在评论区打出来,进行讨论,或者联系我。上述内容全是我自己理解的,如果你有别的想法,或者认为我的理解不对,欢迎指出!!!如果可以,可以点一个免费的赞支持一下吗?谢谢各位彦祖亦菲!!!!!

相关文章:

  • UI 原型设计:交互规则的三要素——重要性、原则与实践
  • 浅析 Golang 内存管理
  • K8S redis 部署
  • nvrtc环境依赖
  • 数据库常见故障排查
  • Java GUI开发全攻略:Swing、JavaFX与AWT
  • 深入理解SpringBoot中的SpringCache缓存技术
  • 2025年PMP 学习十二 第9章 项目资源管理
  • iOS 阅后即焚功能的实现
  • “海外滴滴”Uber的Arm迁移实录:重构大规模基础设施​
  • 前端实践:打造高度可定制的Vue3时间线组件——图标、节点与连接线的个性化配置
  • Keil5 MDK 安装教程
  • 医学影像系统的集成与工作流优化
  • 数据结构中的高级排序算法
  • 【C++设计模式之Decorator装饰模式】
  • PPO算法:一种先进的强化学习策略
  • WeakAuras Lua Script ICC (BarneyICC)
  • Python中列表(list)知识详解(2)和注意事项以及应用示例
  • lua 作为嵌入式设备的配置语言
  • java加强 -stream流
  • 新闻1+1丨城市,如何对青年更友好?
  • 鄂州交警通报致1死2伤车祸:女子操作不当引发,已被刑拘
  • 江西贵溪:铜板上雕出的国潮美学
  • 黄仕忠丨戏曲文献研究之回顾与展望
  • 中拉论坛第四届部长级会议将举行,外交部介绍情况
  • 人民日报读者点题·共同关注:今天我们为什么还需要图书馆?