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

(C语言)超市管理系统 (正式版)(指针)(数据结构)(清屏操作)(文件读写)

目录

前言:

源代码:

product.h

 product.c

 fileio.h

 fileio.c

 main.c

代码解析:

一、程序结构概述

二、product.c 函数详解

1. 初始化商品列表 Init_products

2. 添加商品 add_product

3. 显示商品 display_products

4. 修改商品 mod_product

5.删除函数 del_product

6.查询函数 que_product

三、main.c 主函数详解

1. 主函数 main

2. 辅助函数 clear_screen

四、fileio.c 文件详解

1. 保存数据到文件 save_to_file

2. 加载数据 load_from_file

3. main.c 主函数协同工作

4.联合调试示例

五、核心知识点总结

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. 显示并修改:打印原信息,逐项修改。

为什么这样写

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

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

如何使用

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

输入示例

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

5.删除函数 del_product
//删除单个商品
void del_product(ProductList* list) {//1.显示所有商品display_products(list);printf("--------------------------------------------\n");//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++) {//此时的i+1就是当前商品IDif (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.重新生成商品IDif (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连续。


实现步骤

  1. 显示所有商品

    • 调用 display_products 显示当前所有商品信息,供用户参考。

  2. 输入要删除的ID

    • 用户输入目标商品的ID。

  3. 判断ID是否存在

    • 检查输入的ID是否超过当前商品总数(id_0 > list->count),若超过则提示不存在。

  4. 查找目标商品的索引

    • 遍历商品列表,找到与输入ID匹配的商品索引 i

  5. 删除商品并调整数组

    • 将索引 i 之后的商品依次前移一位,覆盖目标商品。

  6. 更新商品数量

    • 减少 list->count 以反映删除后的商品总数。

  7. 重新生成所有商品的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)。


如何使用

del_product(&list);  // 删除ID为2的商品

输入示例

(显示所有商品)
--------------------------------------------
请输入要删除的ID:2
删除成功!

6.查询函数 que_product
//查询单个商品
void que_product(ProductList* list) {//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) {//此时的i+1就是当前商品IDbreak;}}//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查找并显示指定商品的详细信息。


实现步骤

  1. 判断列表是否为空

    • 若商品数量为0,直接提示库存为空。

  2. 输入要查询的ID

    • 用户输入目标商品的ID。

  3. 判断ID是否存在

    • 检查输入的ID是否超过当前商品总数(id_0 > list->count),若超过则提示不存在。

  4. 查找目标商品的索引

    • 遍历商品列表,找到与输入ID匹配的商品索引 i

  5. 显示商品信息

    • 以表格形式输出该商品的ID、名称、单价和库存。


为什么这样写

  • 快速存在性检查:假设ID连续,通过比较输入ID与 list->count 快速过滤无效ID。

  • 直接遍历查找:线性搜索整个数组,逻辑简单但效率较低(时间复杂度为O(n))。

  • 格式化输出:保持与 display_products 一致的表格布局,提升用户体验。


如何使用

que_product(&list);  // 查询ID为3的商品

输入示例

请输入要搜索的ID:3
搜索成功!ID 名称                 单价    库存
--------------------------------------------3 面包               5.50    30

三、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();  // 清空屏幕后显示新内容

四、fileio.c 文件详解


1. 保存数据到文件 save_to_file
void 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);
}

功能
将商品列表数据保存到二进制文件中,确保程序退出后数据不丢失。

逐行解析

  1. 打开文件

    • "wb":二进制写入模式,清空原文件内容。若文件不存在则新建。

    • fopen 失败时,perror 输出具体错误(如权限不足),exit 终止程序。

  2. 写入商品数量

    • fwrite(&list->count, ...):将商品数量(int 类型)写入文件开头。

    • 作用:后续读取时,根据此值分配内存。

  3. 写入商品数组

    • fwrite(list->Data, ...):将整个商品数组写入文件。

    • 二进制优势:直接写入内存数据,无需格式转换,高效且保留浮点精度。

  4. 关闭文件

    • fclose:确保数据从缓冲区写入磁盘。

关键知识点

  • 二进制文件格式
    文件内容为原始内存数据,不可直接阅读,但读写速度快。

  • 数据持久化:程序退出后,数据通过文件保存,下次启动可恢复。

使用示例

ProductList list;
// ...添加商品...
save_to_file("data.dat", &list); // 保存数据

注意事项

  • 跨平台问题:不同系统可能结构体内存对齐不同,导致文件不兼容。

  • 文件损坏风险:若写入过程被中断(如程序崩溃),文件可能损坏。


2. 加载数据 load_from_file
void 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);
}

功能
从二进制文件加载商品数据到内存,恢复程序上次运行状态。

逐行解析

  1. 初始化列表
    Init_products 清空现有数据,防止残留值干扰。

  2. 打开文件
    "rb":二进制读取模式,文件不存在时返回 NULL,跳过加载。

  3. 读取数量
    fread(&list->count, ...):从文件开头读取商品数量。

  4. 分配内存
    malloc 根据商品数量分配足够内存,失败时终止程序。

  5. 读取数据
    fread 将文件中的商品数据直接读入 Data 数组。

  6. 关闭文件
    释放文件资源。

关键知识点

  • 防御性编程:加载前初始化列表,避免脏数据。

  • 内存管理:动态分配的内存需在退出时通过 free 释放。

使用示例

ProductList list;
load_from_file("data.dat", &list); // 加载数据

注意事项

  • 文件验证缺失:若文件被篡改(如数量与实际数据不符),程序会崩溃。

  • 字节序问题:跨平台时需处理大小端差异(如从Windows写,Linux读)。

3. main.c 主函数协同工作

1. 主函数代码片段

int 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. 数据生命周期管理

  1. 启动流程

    • Init_products:初始化空列表。

    • load_from_file:尝试加载数据,文件不存在则保持空列表。

  2. 运行期间

    • 用户通过菜单操作增删改查,所有变动仅在内存中。

  3. 退出流程

    • save_to_file:将内存数据保存到文件。

    • free(list.Data):释放动态数组内存,防止泄漏。

3. 关键设计思想

  • 数据持久化:通过文件实现“记忆功能”,关闭程序不丢数据。

  • 资源管理

    • 加载时分配内存,退出时释放,遵循“谁分配谁释放”原则。

    • 文件操作封装为独立函数,提高代码可维护性。


4.联合调试示例

1. 正常流程验证

  1. 第一次运行

    添加商品:名称=苹果,单价=5.5,库存=20
    保存退出 → 生成data.dat文件
  2. 文件内容

    • 前4字节:01 00 00 00(数量1)

    • 后续内容:01 00 00 00(ID=1) + 名称、单价、库存的二进制数据。

  3. 第二次运行

    • 自动加载文件,显示已有商品。

    • 修改库存为30后保存退出。

2. 异常场景处理

  • 文件被删除:启动时加载失败,列表为空。

  • 文件损坏:若手动修改文件导致数据错乱,程序可能崩溃。

五、核心知识点总结


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

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

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


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

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


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

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


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

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

相关运行截图: 

 

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

相关文章:

(C语言)超市管理系统 (正式版)(指针)(数据结构)(清屏操作)(文件读写)

目录 前言&#xff1a; 源代码&#xff1a; product.h product.c fileio.h fileio.c main.c 代码解析&#xff1a; 一、程序结构概述 二、product.c 函数详解 1. 初始化商品列表 Init_products 2. 添加商品 add_product 3. 显示商品 display_products 4. 修改商品 mo…...

Framebuffer显示bmp图片

代码&#xff1a; /* 标准输入输出头文件&#xff0c;提供文件操作和输入输出函数&#xff08;如printf&#xff09;*/ #include <stdio.h>/* 文件控制操作头文件&#xff0c;提供文件打开模式&#xff08;如O_RDWR&#xff09;和文件控制函数 */ #include <fcntl.h&…...

常用负载均衡技术有哪些?不同网络层面上的网络负载均衡技术

前言 负载均衡是一种策略&#xff0c;它能让多台服务器或多条链路共同承担一些繁重的计算或I/O任务&#xff0c;从而以较低成本消除网络瓶颈&#xff0c;提高网络的灵活性和可靠性。 在系统管理员发现网络性能不好时&#xff0c;可以通过网络负载均衡来分配资源&#xff0c;以…...

由于复制槽导致wal大量堆积的处理方案

文章目录 环境症状问题原因解决方案 环境 系统平台&#xff1a;N/A 版本&#xff1a;N/A 症状 数据库中的pg_wal占用大量空间&#xff0c;且不删除。 问题原因 复制槽占用早期的wal日志&#xff0c;导致wal归档后无法正常删除。 1. 排查复制槽情况&#xff1a; highgo# …...

用FileCodeBox打造私有文件传输:Ubuntu环境保姆级部署教程!

文章目录 前言1.Docker部署2.简单使用演示3. 安装cpolar内网穿透4. 配置公网地址5. 配置固定公网地址 前言 在数字化浪潮席卷全球的当下&#xff0c;文件传输已成为现代职场的高频需求。当谈及资料交换场景时&#xff0c;许多用户往往抱怨传统工具存在界面复杂、功能卡顿、广告…...

从卡顿到丝滑:JavaScript性能优化实战秘籍

引言 在当今的 Web 开发领域&#xff0c;JavaScript 作为前端开发的核心语言&#xff0c;其性能表现对网页的加载速度、交互响应以及用户体验有着举足轻重的影响。随着 Web 应用的复杂度不断攀升&#xff0c;功能日益丰富&#xff0c;用户对于网页性能的期望也越来越高。从电商…...

win11平台下的docker-desktop中的volume位置问题

因为需要搞个本地的mysql数据库&#xff0c;而且本地安装的程序较多&#xff0c;不想再安mysql了&#xff0c;就想到使用docker来安装。而且因为数据巨大&#xff0c;所以想到直接使用转移data文件夹的方式。 各种查询&#xff0c;而且还使用ai查询&#xff0c;他们都提到&…...

掌握Multi-Agent实践(七):基于AgentScope分布式模式实现多智能体高效协作[并行加速大模型辅助搜索、分布式多用户协同辩论赛]

之前的案例都是运行在单台机器上以单进程形式运行,受限于 Python 的全局解释器锁,实际只能有效利用一个 CPU 的计算资源,并且无法支持多个用户从自己的电脑上接入同一个 Multi-Agent 应用进行交互。为了提高运行效率并支持多用户接入同一个应用中,AgentScope 提供了分布式…...

2023CCPC河南省赛暨河南邀请赛个人补题ABEFGHK

Dashboard - 2023 CCPC Henan Provincial Collegiate Programming Contest - Codeforces 过题难度&#xff1a;A H F G B K E 铜奖&#xff1a; 2 339 银奖&#xff1a; 3 318 金奖&#xff1a; 5 523 A: 直接模拟 // Code Start Here int t;cin >> t;while(t-…...

基于基金净值百分位的交易策略

策略来源&#xff1a;睿思量化小程序 基金净值百分位&#xff0c;是衡量当前基金净值在过去一段时间内的相对位置。以近一年为例&#xff0c;若某基金净值百分位为30%&#xff0c;意味着过去一年中有30%的时间基金净值低于当前值&#xff0c;70%的时间高于当前值。这一指标犹如…...

Ubuntu20.04下如何源码编译Carla,使用UE4源码开跑,踩坑集合

一、简介 作为一个从事算法研究的人员,无人驾驶仿真一直是比较重要的一部分,但是现在比较常见的算法验证都是在carla这个开源仿真平台上做的,所以我有二次开发carla的需求,今天就来讲讲编译CARLA。 网上的教材很多,但还是推荐大家看官网教程:Linux build - CARLA Simul…...

Windows 环境下 Docker Desktop 安装 + 汉化

一、WSL2Ubuntu20.04以及Docker Desktop安装 在开发过程中&#xff0c;Docker 是一款极为实用的工具。本教程专为 Windows 用户打造&#xff0c;将全面且细致地讲解 Docker Desktop 的安装与配置步骤&#xff0c;助您迅速入门并熟练掌握。同时&#xff0c;考虑到语言使用习惯&…...

HDMI信号采集器连OBS没有声音的问题

Windows中的设置 windows中可以按客服发来的视频做参考。 视频教程&#xff1a; https://jvod.300hu.com/vod/product/79b234d5-81f0-4255-8680-693de7b4b0d4/715859bc4d83490bb977d8bc0061bd5e.mp4 Linux中的设置 在linux中发现视频不太一样&#xff0c;设置完没有声音。…...

Remote Desktop安卓远程无法使用中文输入法

新版remote desktop包括windows app都无法使用中文输入法 下载旧版才能用8.8.1.56.294 感谢评论区大哥提供的方案...

maven报错 You have to use a classifier to attach supplemental artifacts

完整报错 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-jar-plugin:3.3.0:jar (default-jar) on project **: You have to use a classifier to attach supplemental artifacts to the project instead of replacing them. -> [Help 1] 解决办法 修改…...

maven dependencyManagement标签作用

dependencyManagement作用 1. 确保多模块间依赖版本一致 在父 POM 中定义依赖的版本、作用域&#xff08;Scope&#xff09;、排除项&#xff08;Exclusions&#xff09;等&#xff0c;子模块引用这些依赖时无需指定版本 2.灵活覆盖 子模块可以按需覆盖父 POM 中定义的版…...

Flink 运维监控与指标采集实战(Prometheus + Grafana 全流程)

一、引言:为什么 Flink 运维监控如此重要? 在实时计算场景中,Flink 作业 724 小时运行,对性能、资源、故障感知、状态变化的实时监控非常关键。没有有效的运维可观测体系: 不知道任务是否在稳定运行 发生问题难以快速定位 无法感知背压、延迟、反压等状态 因此,构建完善…...

计算机视觉----基于锚点的车道线检测、从Line-CNN到CLRNet到CLRKDNet 本文所提算法Line-CNN 后续会更新以下全部算法

本文所提算法如下&#xff1a; 叙述按时间顺序 你也可以把本文当作快速阅读这几篇文献的一个途径 所有重要的部分我都已经标注并弄懂其原理 方便自己也是方便大家 Line-CNN&#xff1a;基于线提议单元的端到端交通线检测 摘要 交通线检测是一项基础且具有挑战性的任务。以往的…...

Spring MVC HttpMessageConverter 的作用是什么?

HttpMessageConverter (HTTP 消息转换器) 是 Spring MVC 框架中一个非常核心的组件&#xff0c;它的主要作用是在 HTTP 请求、响应体与 Java 对象之间进行双向转换。 核心作用&#xff1a; 读取请求体 (Request Body) 到 Java 对象&#xff1a; 当 Controller 方法的参数使用 …...

WebGL 3着色器和GLSL

我们之前提到过着色器和GLSL&#xff0c;但是没有涉及细节&#xff0c;你可能已经对此有所了解&#xff0c; 但以防万一&#xff0c;这里将详细讲解着色器和GLSL。 在工作原理中我们提到&#xff0c;WebGL每次绘制需要两个着色器&#xff0c; 一个顶点着色器和一个片段着色器&…...

Hugging Face 中 LeRobot 使用的入门指南

相关源文件 .github/ISSUE_TEMPLATE/bug-report.yml .github/PULL_REQUEST_TEMPLATE.md README.md examples/1_load_lerobot_dataset.py examples/2_evaluate_pretrained_policy.py examples/3_train_policy.py lerobot/scripts/eval.py lerobot/scripts/train.py 本页面提供 …...

从零开始学习three.js(18):一文详解three.js中的着色器Shader

在WebGL和Three.js的3D图形渲染中&#xff0c;着色器&#xff08;Shader&#xff09; 是实现复杂视觉效果的核心工具。通过编写自定义的着色器代码&#xff0c;开发者可以直接操作GPU&#xff0c;实现从基础颜色渲染到动态光照、粒子效果等高级图形技术。本文将深入解析Three.j…...

Trae - 国人Cursor的免费平替产品

前情提要&#xff1a;Cursor-零基础使用flutter完成贪吃蛇游戏-迄今为止世上最牛的AI编辑工具&#xff0c;不需要程序员也可以编程 Cursor 不是我的最佳选择 Cursor 是基于 VSCode 进化而来&#xff0c;虽然好用&#xff0c;但总结下来有几点点是我有所顾虑的。 第一&#x…...

【PmHub后端篇】PmHub中Seata分布式事务保障任务审批状态一致性

在分布式系统中&#xff0c;事务管理是保证数据一致性的关键。本文将深入探讨在PmHub系统中&#xff0c;如何利用Seata分布式事务来保证任务审批状态的一致性。分布式事务在面试中是常见的考点&#xff0c;网上教程多偏理论&#xff0c;而实际项目中的应用更为关键。 1 事务基…...

2025年长三角高校数模竞赛B题Q1-Q3详细求解与Q4详细分析

B题 空气源热泵供暖的温度预测 问题背景 空气源热泵是一种与中央空调类似的设备&#xff0c;其结构主要由压缩主机、热交换 器以及末端构成&#xff0c;依靠水泵对末端房屋提供热量来实现制热。空气源热泵作为热 惯性负载&#xff0c;调节潜力巨大。工作时通过水循环系统将…...

插槽(Slot)的使用方法

插槽是Vue.js中一个强大的功能&#xff0c;允许你在组件中预留位置&#xff0c;让父组件可以插入自定义内容。以下是插槽的主要使用方法&#xff1a; 基本插槽 <!-- 子组件 ChildComponent.vue --> <template><div><h2>子组件标题</h2><slo…...

Milvus Docker 部署教程

1. 环境准备 确保您的系统已安装&#xff1a; DockerDocker ComposePython (用于运行客户端代码) 2. 项目结构 创建项目目录并设置以下文件&#xff1a; milvus-docker/ ├── docker-compose.yml ├── main.py3. 配置文件 docker-compose.yml version: 3.5services:…...

Vue 3 中 watch 的使用与深入理解

在 Vue 3 的 Composition API 中&#xff0c;watch 是一个非常强大的工具&#xff0c;用于监听响应式数据的变化并做出相应的处理。本文将通过一段实际代码来深入解析 watch 的行为和使用技巧。 &#x1f9e9; 示例代码回顾 import { reactive, watch } from vueconst state …...

嵌入式学习笔记 D21:双向链表的基本操作

双向链表的定义与创建双向链表的插入双向链表的查找双向链表的修改双向链表的删除双向链表的逆序MakeFile工具使用 一、双向链表的定义与创建 1.双向链表的定义&#xff1a; 双向链表是在单链表的每个结点中&#xff0c;再设置一个指向其前一个结点的指针域。 struct DOUNode…...

pciutils-3.5.5-win64工具的使用方法

目录 简介&#xff1a; 方法一&#xff1a;使用设备管理器 方法二&#xff1a;pciutils-3.5.5-win64 简介&#xff1a; window系统下查看PCIe设备信息比较困难 linux版本下&#xff0c;查看PCIE的信息比较容易&#xff0c;可在安装插件后直接使用命令得出。 例如&#xf…...

全息美AISEO引领AIGEO新趋势

内容概要 在数字化营销变革的浪潮中&#xff0c;全息美AISEO为企业注入了全新的活力。通过结合先进的技术与创造性的策略&#xff0c;AISEO不仅提升了品牌的可见度&#xff0c;更通过精准的用户定位&#xff0c;实现了信息的高效传播。尤其在当下竞争日益激烈的市场环境中&…...

平滑过滤值策略

该策略是一种基于技术分析的交易策略,主要通过计算一系列指标来判断市场趋势,并根据这些指标生成交易信号。 策略概述 该策略的核心在于利用多个技术指标来分析市场动态,并据此制定交易决策。它结合了价格动量、波动性和趋势跟踪等多种因素,旨在提高交易的准确性和效率。…...

Windows平台OpenManus部署及WebUI远程访问实现

前言&#xff1a;继DeepSeek引发行业震动后&#xff0c;Monica.im团队最新推出的Manus AI 产品正席卷科技圈。这款具备自主思维能力的全能型AI代理&#xff0c;不仅能精准解析复杂指令并直接产出成果&#xff0c;更颠覆了传统人机交互模式。尽管目前仍处于封闭测试阶段&#xf…...

极验证Geetest 通过python代理 透传

看了几个破解Geetest 四代滑块的视频和文章&#xff0c;有点望而生畏&#xff0c;github上也有收集极验四代滑块所有图片后经过ai训练做的破解代码&#xff0c;但作者说成功率只有90%。 于是考虑通过python透传的方法&#xff0c;j就是对自动化脚本套个【网页界面】的壳&#…...

JDK 1.8 全解析:从核心特性到企业实战的深度实践

引言 JDK 1.8 作为 Java 生态发展史上的里程碑版本&#xff0c;自 2014 年发布以来&#xff0c;凭借 Lambda 表达式、Stream API、新日期时间 API 三大核心特性&#xff0c;彻底重塑了 Java 编程范式。本文结合 Oracle 官方文档、蚂蚁集团、京东零售等企业级实战案例&#xff…...

OptiStruct实例:汽车声控建模

本章通过一个Tim-BaDy模型&#xff0c;展示利用AemmiecaviyMet工具进行声腔网格划分的过程因为南腔是考康边界的声场&#xff0c;所以在开展腔建机之的N营涉设物建大致封闭的边界模器东有内商院建权为例&#xff0c;首先需要准备自车身所合之的、首先委部)结构的有限元型车内声…...

k8s(12) — 版本控制和滚动更新(金丝雀部署理念)

金丝雀部署简介&#xff1a; 1、基本概念 金丝雀部署是一种软件开发中的渐进式发布策略&#xff0c;其核心思想是通过将新版本应用逐步发布给一小部分用户&#xff08;即 “金丝雀” 用户&#xff09;&#xff0c;在真实环境中验证功能稳定性和性能表现&#xff0c;再逐步扩大发…...

基于matlab实现AUTOSAR软件开发---答疑6

最近有少朋友在咨询我关于模型生成A2L文件,之后在把elf文件的地址提取进去,生成最终的A2L的操作,这个其实可以看下mathwork的帮助文档,地址如下: https://www.mathworks.com/help/ecoder/ug/asap2-cdf-calibration.htmlhttps://www.mathworks.com/help/ecoder/ug/asap2-c…...

铜墙铁壁 - 服务网格的安全之道 (Istio 实例)

铜墙铁壁 - 服务网格的安全之道 (Istio 实例) 在微服务架构中,服务间的通信是频繁且复杂的。传统的安全模型常常假设内部网络是可信的,这在现代分布式系统和云原生(尤其是零信任)环境中是远远不够的。我们需要解决几个核心安全问题: 通信加密 (Encryption):如何确保服务 …...

计量——检验与代理变量

1.非嵌套模型的检验 1Davidson-Mackinnon test 判断哪个模型好 log&#xff08;y&#xff09;β0β1x1β2x2β3x3u log&#xff08;y&#xff09;β0β1log&#xff08;x1&#xff09;β2log&#xff08;x2&#xff09;β3log&#xff08;x3&#xff09;u 1.对log&#xff…...

【C++】解析C++面向对象三要素:封装、继承与多态实现机制

解析C面向对象三要素&#xff1a;封装、继承与多态实现机制 1. 面向对象设计基石2. 封装&#xff1a;数据守卫者2.1 访问控制实现2.2 封装优势 3. 继承&#xff1a;代码复用艺术3.1 继承的核心作用3.2 继承类型对比3.3 典型应用场景3.4 构造函数与析构函数处理3.4.1 构造顺序控…...

c语言 写一个五子棋

c语言 IsWin判赢 display 画棋盘 判断落子的坐标是否已有棋子 判断落子坐标范围是否超出范围 // 五子棋 #include <stdio.h> #include <stdlib.h>// 画棋盘 void display(char map[][10]) {system("clear");printf(" 0 1 2 3 4 5 6 7 8 9\n&…...

深度解析 IDEA 集成 Continue 插件:提升开发效率的全流程指南

一、插件核心功能与应用场景 Continue 是一款专为 JetBrains IDE 设计的 AI 编程助手插件&#xff0c;基于大语言模型实现以下核心功能&#xff1a; 智能代码生成&#xff1a;支持根据自然语言描述生成完整方法、单元测试或设计模式&#xff08;如线程安全单例模式&#xff0…...

Node.js

本文来源 &#xff1a; 腾讯元宝 Node.js 是一个基于 ​​Chrome V8 引擎​​ 的 JavaScript 运行时环境&#xff0c;采用 ​​事件驱动​​ 和 ​​非阻塞 I/O​​ 模型&#xff0c;专为构建高性能、可扩展的网络应用而设计。以下是其核心特性和应用场景的总结&#xff1a; 1…...

idea经常卡顿解决办法

一&#xff1a;前言 &#xff08;1&#xff09;使用idea工具开发过久&#xff0c;出现卡顿&#xff0c;等待响应 二&#xff1a;原因 &#xff08;1&#xff09;给idea设置的运行内存过小&#xff0c;需要使用的内存超过设置的内存 &#xff08;2&#xff09;插件过多&…...

【python】字典:: a list of dictionaries

No, actions is not a dictionary. It’s a list of dictionaries. Each item in the list is a dictionary with three key-value pairs: “measure” (number), “resource” (string), and “reason” (string). Here’s the structure: actions is a list []Each element…...

高效电脑隐私信息清理实用工具

软件介绍 本文介绍的这款Privacy Eraser&#xff0c;它是一款电脑系统隐私清理工具。 功能介绍 这款工具能够清理电脑里的多种信息&#xff0c;比如最近文件、临时文件、注册表信息&#xff0c;还有浏览器插件以及日志文件等等。 引导提示 注重隐私保护的小伙伴一定要将这款…...

在ubuntu系统中将vue3的打包文件dist 部署nginx 并且配置ssl证书 以https方式访问

在ubuntu系统中将vue3的打包文件dist 部署nginx 并且配置ssl证书 以https方式访问 确保 Nginx 已安装准备 Vue 3 打包文件配置 Nginx编辑 Nginx 配置文件启用配置文件测试 Nginx 配置重新加载 Nginx配置 SSL 证书获取 SSL 证书验证证书自动续期验证部署注意事项 确保 Nginx 已安…...

MH22D3开发高级UI应用,适配arm2d驱动

在资源有限的嵌入系统上&#xff0c;要开发出具有现代风格&#xff08;圆弧&#xff0c;表盘&#xff0c;滚动&#xff0c;滑动&#xff0c;透明&#xff0c;图层叠加等&#xff09;的UI应用&#xff0c;需要极高的cpu算力和ram&#xff0c;flash资源的支持。 但是往往鱼和熊掌…...

MongoDB数据库深度解析:架构、特性与应用场景

在现代应用程序开发中&#xff0c;数据存储技术的选择至关重要。在众多的数据库管理系统中&#xff0c;MongoDB以其灵活性和强大的功能迅速崛起&#xff0c;成为NoSQL数据库中的佼佼者。本文将深入解析MongoDB的架构、核心特性、性能优化及其在实际应用中的最佳实践&#xff0c…...