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

news2025/5/17 5:35:19

目录

前言:

源代码:

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.输入要修改的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

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

//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");
        }
    }
}

代码解析:

一、程序结构概述

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

  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. 自动生成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。

实现步骤

  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    20
    2 香蕉               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.输入要删除的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连续。


实现步骤

  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.输入要搜索的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查找并显示指定商品的详细信息。


实现步骤

  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 _WIN32
    system("cls");   // Windows清屏
#else
    system("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" 确保数据精确存储。

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

相关运行截图: 

 

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2377369.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

NAT转换和ICMP

NAT nat原理示意 nat实现 ICMP ICMP支持主机或路由器&#xff1a; 差错或异常报告网络探寻 2类icmp报文&#xff1a; 差错报告报文&#xff08;5种&#xff09; 目的不可达源抑制--拥塞控制超时&超期--TTL超时参数问题--问题报文丢弃重定向--不应该由这个路由器转发&a…

【专利信息服务平台-注册/登录安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…

BUUCTF——web刷题第一页题解

共31题&#xff0c;admin那题没有&#xff0c;因为环境问题&#xff0c;我做的非常卡 目录 极客大挑战 2019]Havefun [HCTF 2018]WarmU [ACTF2020 新生赛]Include [ACTF2020 新生赛]Exec [GXYCTF2019]Ping Ping Ping [SUCTF 2019]EasySQL [极客大挑战 2019]LoveSQL [极…

哪个品牌的智能对讲机好用?推荐1款,能扛事更智能

在专业通信领域&#xff0c;智能对讲机早已突破传统设备的局限&#xff0c;成为集通信、调度、数据传输于一体的智能化终端。面对复杂多变的作业环境&#xff0c;用户对设备的稳定性、通信效率和智能化水平提出了更高要求。但是&#xff0c;市面上产品同质化严重&#xff0c;部…

【Win32 API】 lstrcpyA()

作用 将字符串复制到指定的字符串缓冲区。 函数 LPSTR lstrcpyA(LPSTR lpString1, LPCSTR lpString2); 参数 lpString1 类型&#xff1a;LPTSTR 一个缓冲区&#xff0c;用于接收由 lpString2 参数指向的字符串的内容。 缓冲区必须足够大才能包含字符串&#xff0c;包括终止…

Vue3——Watch侦听器

目录 手动指定监听对象 侦听ref对象 侦听ref对象中的某个属性 reactive写法 watchEffect 自动侦听 多源侦听 一次性侦听器 watch 是⼀个⽤于观察和响应Vue响应式系统中数据变化的⽅法。它允许你指定⼀个数据源&#xff08;可以是 响应式引⽤、计算属性、组件的属性等&#xf…

Go的单测gomock及覆盖率命令

安装gomock&#xff1a; go get github.com/golang/mock/gomockgo get github.com/golang/mock/mockgen 使用 mockgen 生成 mock 代码: 参考 mockgen -sourceservice/user.go -destinationservice/mocks/mock_user_service.go -packagemocks go test -coverprofilecoverage.out…

Leetcode209做题笔记

力扣209 题目分析&#xff1a;想象一个窗口遍历着这个数组&#xff0c;不断扩大右边界&#xff0c;让r。往窗口中添加数字&#xff1a; 此时我们找到了这个窗口&#xff0c;它的和满足了大于等于target的条件&#xff0c;题目让我求最短的&#xff0c;那么我们就尝试来缩短它&…

Suna: 开源多面手 AI 代理

GitHub&#xff1a;GitHub - kortix-ai/suna: Suna - Open Source Generalist AI Agent 更多AI开源软件&#xff1a;发现分享好用的AI工具、AI开源软件、AI模型、AI变现 - 小众AI Suna 是一个完全开源的 AI 助手&#xff0c;可帮助您轻松完成实际任务。通过自然对话&#xff0c…

25-05-16计算机网络学习笔记Day1

深入剖析计算机网络&#xff1a;今日学习笔记总结 本系列博客源自作者在大二期末复习计算机网络时所记录笔记&#xff0c;看的视频资料是B站湖科大教书匠的计算机网络微课堂&#xff0c;每篇博客结尾附书写笔记(字丑见谅哈哈) 视频链接地址 一、计算机网络基础概念 &#xf…

12 web 自动化之基于关键字+数据驱动-反射自动化框架搭建

文章目录 一、如何实现一条用例&#xff0c;实现覆盖所有用例的测试1、结合数据驱动&#xff1a;编辑一条用例&#xff0c;外部导入数据实现循环测试2、用例体&#xff1a;实现不同用例的操作步骤对应的断言 二、实战1、项目路径总览2、common 文件夹下的代码文件3、keywords 文…

动态IP赋能业务增效:技术解构与实战应用指南

在数字化转型加速的今天&#xff0c;IP地址作为网络通信的基础设施&#xff0c;其技术特性正深刻影响着企业业务架构的效率与安全性。动态IP&#xff08;Dynamic IP&#xff09;作为互联网资源分配的核心机制&#xff0c;早已突破传统认知中的"临时地址"定位&#xf…

【Java ee初阶】http(1)

HTTP 全称为“超文本传输协议”&#xff0c;由名字可知&#xff0c;这是一个基于文本格式的协议&#xff0c;而TCP&#xff0c;UDP&#xff0c;以太网&#xff0c;IP...都是基于二进制格式的协议。 如何区别该协议是基于哪种格式的协议&#xff1f; 形如这种协议格式&#xf…

day18-数据结构引言

一、 概述 数据结构&#xff1a;相互之间存在一种或多种特定关系的数据元素的集合。 1.1 特定关系&#xff1a; 1. 逻辑结构 2.物理结构&#xff08;在内存当中的存储关系&#xff09; 逻辑结构物理结构集合&#xff0c;所有数据在同一个集合中&#xff0c;关系平等顺…

我开源了一个免费在线工具!UIED Tools

UIED Tools - 免费在线工具集合 最近更新&#xff1a;修改了文档说明&#xff0c;优化了项目结构介绍 这是设计师转开发的第一个开源项目&#xff0c;bug和代码规范可能有些欠缺。 这是一个功能丰富的免费在线工具集合网站&#xff0c;集成了多种实用工具&#xff0c;包括 AI …

什么时候可以开始学习深度学习?

咱们先来聊聊机器学习和深度学习的关系~ 这个问题其实挺常见的&#xff0c;之前我也跟不少同事、同学聊过。最近有好几个同学也聊过。 简单说&#xff0c;深度学习是机器学习的一个子集&#xff0c;两者不是并列关系&#xff0c;而是“包含”关系。 你可以这么理解&#xff…

初学python的我开始Leetcode题8-5

提示&#xff1a;100道LeetCode热题-8-5主要是二叉树相关&#xff0c;包括三题&#xff1a;路径总和 III、二叉树的最近公共祖先、二叉树中的最大路径和。由于初学&#xff0c;所以我的代码部分仅供参考。 前言 二叉树完结撒花~ 下一次的图论会是一些有趣的应用案例~ 提示&am…

构建RAG混合开发---PythonAI+JavaEE+Vue.js前端的实践

写在前文&#xff1a;之所以设计这一套流程&#xff0c;是因为 Python在前沿的科技前沿的生态要比Java好&#xff0c;而Java在企业级应用层开发比较活跃&#xff1b; 毕竟许多企业的后端服务、应用程序均采用Java开发&#xff0c;涵盖权限管理、后台应用、缓存机制、中间件集成…

08.webgl_buffergeometry_attributes_none ,three官方示例+编辑器+AI快速学习

本实例主要讲解内容 这个Three.js示例展示了无属性几何体渲染技术&#xff0c;通过WebGL 2的gl_VertexID特性和伪随机数生成算法&#xff0c;在着色器中动态计算顶点位置和颜色&#xff0c;而不需要在CPU端预先定义几何体数据。 核心技术包括&#xff1a; WebGL 2的顶点ID特…

26考研 | 王道 | 计算机组成原理 | 一、计算机系统概述

26考研 | 王道 | 计算机组成原理 | 一、计算机系统概述 文章目录 26考研 | 王道 | 计算机组成原理 | 一、计算机系统概述1.1 计算机的发展1.2 计算机硬件和软件1.2.1 计算机硬件的基本组成1.2.2 各个硬件的工作原理1.2.3 计算机软件1.2.4 计算机系统的层次结构1.2.5 计算机系统…