(C語言)超市管理系統(測試版)(指針)(數據結構)(二進制文件讀寫)

目錄

前言:

源代碼:

product.h

?product.c

?fileio.h

?fileio.c

?main.c

代碼解析:

fileio模塊(文件(二進制))

?寫文件(保存)

函數功能

代碼逐行解析

關鍵知識點

讀文件(加載)?

函數功能

代碼逐行解析

關鍵知識點

mian模塊?(free釋放內存)

1. 為什么需要這行代碼?

內存泄漏問題

代碼中的具體場景

2.?free(list.Data)?的作用

釋放堆內存

內存示意圖

3. 何時調用?free()?

正確時機

忘記釋放的后果

4. 為什么不需要釋放?List?變量本身?


前言:

當前這篇博客是測試版,僅僅教大家讀寫二進制文件的相關知識點!

共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);//初始化商品列表結構體

?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
}

?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 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 mian() {//1.創建結構體并初始化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://add_product(&list);break;case 2://display_products(&list);break;case 6:save_to_file(FILENAME, &list); // 保存數據free(list.Data); // 釋放動態內存printf("系統已退出\n");return 0; // 正確退出default:printf("無效輸入\n");}}
}

代碼解析:

fileio模塊(文件(二進制))

?寫文件(保存)

save_to_file?函數解析

函數功能

將?ProductList?中的商品數據保存到二進制文件中。

代碼逐行解析
// 保存數據到文件(二進制寫入)
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);
}
關鍵知識點
  1. 二進制文件操作

    • "wb"?模式直接寫入內存數據,保持精確性(如浮點數不會丟失精度)

    • 文件內容不可直接閱讀,但讀寫效率高

  2. 數據存儲順序
    文件結構如下:

    ┌──────────────┬───────────────────────────┐
    │ 4字節整數     │ N個Product結構體          │
    │ (商品數量count) │ (每個占sizeof(Product)字節) │
    └──────────────┴───────────────────────────┘
  3. 錯誤處理

    • perror?會輸出類似?保存失敗: Permission denied?的詳細信息

    • exit(EXIT_FAILURE)?立即終止程序,防止后續操作破壞數據

  4. const?修飾符

    • const ProductList* list?保證函數內不會修改結構體內容

讀文件(加載)?

load_from_file?函數解析

函數功能

從二進制文件中加載數據到?ProductList?結構體。

代碼逐行解析
// 從文件加載數據(二進制讀取)
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);
}
關鍵知識點
  1. 安全初始化

    • 進入函數后立刻初始化?list,避免殘留值導致錯誤

  2. 文件打開模式

    • "rb"?表示二進制讀取模式,文件不存在時不會創建新文件

  3. 動態內存管理

    • malloc?根據文件中的商品數量分配精確內存

    • 計算公式:數量 × sizeof(Product)?確保內存足夠存放所有商品

擴展知識:二進制文件 vs 文本文件

特性二進制文件文本文件
存儲方式直接存儲內存數據存儲字符編碼
可讀性不可直接閱讀可用文本編輯器查看
浮點數存儲精確(IEEE 754格式)可能有精度損失
結構體存儲直接整體存儲需要序列化/反序列化
跨平臺兼容性需保證結構體內存布局一致更通用
讀寫效率高(無格式轉換)低(需解析格式)

mian模塊?(free釋放內存)

在 C 語言中,動態分配的內存(通過?malloccalloc?或?realloc?申請的內存)不會自動釋放,必須由程序員手動調用?free()?函數釋放。free(list.Data);?這行代碼的作用是釋放?ProductList?結構體中動態分配的?Data?數組內存。以下是詳細解釋:


1. 為什么需要這行代碼?
內存泄漏問題
  • 動態內存的生命周期:通過?malloc?分配的內存會一直存在,直到程序退出或顯式調用?free()?釋放。

  • 若不釋放:程序每次運行時分配的內存會累積,最終導致內存耗盡(稱為“內存泄漏”)。

  • 示例
    假設每次運行程序都添加 1000 個商品,但退出時不釋放內存:

    • 第一次運行:占用 1000 × sizeof(Product) 字節內存(未釋放)。

    • 第二次運行:再占用 1000 × sizeof(Product) 字節內存(未釋放)。

    • 最終程序會因內存不足崩潰。

代碼中的具體場景
  • Data?數組的內存來源
    在?load_from_file?函數中,通過?malloc?分配內存:

    list->Data = malloc(list->count * sizeof(Product));
  • Data?的所有權
    該內存由?ProductList?結構體管理,退出時必須歸還系統。


2.?free(list.Data)?的作用
釋放堆內存
free(list.Data); // 釋放 Data 數組占用的堆內存
  • 操作對象list.Data?是指向動態分配的數組的指針。

  • 結果
    操作系統回收該內存區域,程序不再能訪問?Data?的內容(訪問會引發未定義行為)。

內存示意圖
程序內存布局:
┌─────────────┐
│  棧區       │ ← list 變量(包括 Data 指針和 count)
├─────────────┤
│  堆區       │ ← list.Data 指向的動態內存(需手動釋放)
└─────────────┘

3. 何時調用?free()
正確時機
  • 在程序不再需要?Data?數組時調用(如退出前)。

  • 在您的主函數中,當用戶選擇“保存并退出”(選項 6)時釋放內存:

    case 6:save_to_file(FILENAME, &List); // 保存數據free(List.Data);              // 釋放內存printf("系統已退出\n");return 0;
忘記釋放的后果
  • 內存泄漏:程序每次運行都會“吃掉”更多內存,最終導致系統資源耗盡。

  • 性能下降:長期運行的程序(如服務器)會逐漸變慢甚至崩潰。


4. 為什么不需要釋放?List?變量本身?
  • List?的內存來源
    ProductList List;?是局部變量,在棧上分配,由系統自動管理。

  • 棧內存特性
    函數退出時,棧內存(包括?List?變量)會自動釋放,無需手動操作。

  • 重點
    只需釋放?List.Data?指向的堆內存,無需釋放?List?本身。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/82853.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/82853.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/82853.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ubuntu----100,常用命令2

目錄 文件與目錄管理系統信息與管理用戶與權限管理網絡配置與管理軟件包管理打包與壓縮系統服務與任務調度硬件信息查看系統操作高級工具開發相關其他實用命令 在 Ubuntu 系統中&#xff0c;掌握常用命令可以大幅提升操作效率。以下是一些常用的命令&#xff0c;涵蓋了文件管理…

WiFi密碼查看器打開軟件自動獲取數據

相信有很大一部分人都不知道怎么看已經連過的WiFi密碼。 你還在手動查詢自己的電腦連接過得WiFi密碼嗎&#xff1f; —————【下 載 地 址】——————— 【本章單下載】&#xff1a;https://drive.uc.cn/s/dbbedf933dad4 【百款黑科技】&#xff1a;https://ucnygalh6…

開目新一代MOM:AI賦能高端制造的破局之道

導讀 INTRODUCTION 在高端制造業智能化轉型的深水區&#xff0c;企業正面臨著個性化定制、多工藝場景、動態生產需求的敏捷響應以及傳統MES柔性不足的考驗……在此背景下&#xff0c;武漢開目信息技術股份有限公司&#xff08;簡稱“開目軟件”&#xff09;正式發布新一代開目…

Android開發-視圖基礎

在Android應用開發中&#xff0c;視圖&#xff08;View&#xff09;是構建用戶界面的基本元素。無論是按鈕、文本框還是復雜的自定義控件&#xff0c;它們都是基于View類或其子類實現的。掌握視圖的基礎知識對于創建功能強大且美觀的應用至關重要。本文將深入探討Android中的視…

無人機信號線被電磁干擾導致停機

問題描述&#xff1a; 無人機飛控和電調之間使用PWM信號控制時候&#xff0c;無人機可以正常起飛&#xff0c;但是在空中懸停的時候會出現某一個電機停機&#xff0c;經排查電調沒有啟動過流過壓等保護&#xff0c;定位到電調和飛控之間的信號線被干擾問題。 信號線被干擾&am…

VSCode設置SSH免密登錄

引言 2025年05月13日20:21:14 原來一直用的PyCharn來完成代碼在遠程服務器上的運行&#xff0c;但是PyCharm時不時同步代碼會有問題。因此&#xff0c;嘗試用VSCode來完成代碼SSH遠程運行。由于VSCode每次進行SSH連接的時候都要手動輸入密碼&#xff0c;為了解決這個問題在本…

硬密封保溫 V 型球閥:恒溫工況下復雜介質控制的性價比之選-耀圣

硬密封保溫 V 型球閥&#xff1a;恒溫工況下復雜介質控制的性價比之選 在瀝青儲運、化學原料加工、食品油脂輸送等工業領域&#xff0c;帶顆粒高粘度介質與料漿的恒溫輸送一直是生產的關鍵環節。普通閥門在應對此類介質時&#xff0c;常因溫度流失導致介質凝結堵塞、密封失效&…

最終一致性和強一致性

最終一致性和強一致性是分布式系統中兩種不同的數據一致性模型&#xff0c;它們在數據同步的方式和適用場景上有顯著區別&#xff1a; 1. 強一致性&#xff08;Strong Consistency&#xff09; 定義&#xff1a;所有節點&#xff08;副本&#xff09;的數據在任何時刻都保持一…

基于單應性矩陣變換的圖像拼接融合

單應性矩陣變換 單應性矩陣是一個 3x3 的可逆矩陣&#xff0c;它描述了兩個平面之間的投影變換關系。在圖像領域&#xff0c;單應性矩陣可以將一幅圖像中的點映射到另一幅圖像中的對應點&#xff0c;前提是這兩幅圖像是從不同視角拍攝的同一平面場景。 常見的應用場景&#x…

如何同步虛擬機文件夾

以下是一些常見的同步虛擬機文件夾的方法&#xff1a; 使用共享文件夾&#xff08;以VMware和VirtualBox為例&#xff09; - VMware&#xff1a;打開虛擬機&#xff0c;選擇“虛擬機”->“設置”&#xff0c;在“選項”中選擇“共享文件夾”&#xff0c;點擊“添加”選擇…

前端流行框架Vue3教程:15. 組件事件

組件事件 在組件的模板表達式中&#xff0c;可以直接使用$emit方法觸發自定義事件 觸發自定義事件的目的是組件之間傳遞數據 我們來創建2個組件。父組件&#xff1a; ComponentEvent.vue,子組件&#xff1a;Child.vue Child.vue <script> export default {// 子組件通…

Python+1688 API 開發教程:實現商品實時數據采集的完整接入方案

在電商行業競爭日益激烈的當下&#xff0c;掌握商品實時數據是企業制定精準營銷策略、優化供應鏈管理的關鍵。1688 作為國內重要的 B2B 電商平臺&#xff0c;其開放平臺提供了豐富的 API 接口&#xff0c;借助 Python 強大的數據處理能力&#xff0c;我們能夠高效實現商品數據的…

聊一聊Electron中Chromium多進程架構

Chromium 多進程架構概述 Chromium 的多進程架構是其核心設計之一&#xff0c;旨在提高瀏覽器的穩定性、安全性和性能。Chromium 將不同的功能模塊分配到獨立的進程中&#xff0c;每個進程相互隔離&#xff0c;避免了單進程架構中一個模塊的崩潰導致整個瀏覽器崩潰的問題。 在…

CodeBuddy 中國版 Cursor 實戰:Redis+MySQL雙引擎驅動〈王者榮耀〉戰區排行榜

文章目錄 一、引言二、系統架構設計2.1、整體架構概覽2.2、數據庫設計2.3、后端服務設計 三、實戰&#xff1a;從零構建排行榜3.1、開發環境準備3.2、用戶與戰區 數據管理3.2.1、MySQL 數據庫表創建3.2.2、實現用戶和戰區數據的 CURD 操作 3.3、實時分數更新3.4、排行榜查詢3.5…

Oracle OCP認證考試考點詳解083系列15

題記&#xff1a; 本系列主要講解Oracle OCP認證考試考點&#xff08;題目&#xff09;&#xff0c;適用于19C/21C,跟著學OCP考試必過。 71. 第71題&#xff1a; 題目 解析及答案&#xff1a; 關于在 Oracle 18c 及更高版本中基于 Oracle 黃金鏡像的安裝&#xff0c;以下哪…

LS-NET-012-TCP的交互過程詳解

LS-NET-012-TCP的交互過程詳解 附加&#xff1a;TCP如何保障數據傳輸 TCP的交互過程詳解 一、TCP協議核心交互流程 TCP協議通過三次握手建立連接、數據傳輸、四次揮手終止連接三大階段實現可靠傳輸。整個過程通過序列號、確認應答、窗口控制等機制保障傳輸可靠性。 1.1 三次…

【Pandas】pandas DataFrame cumprod

Pandas2.2 DataFrame Computations descriptive stats 方法描述DataFrame.abs()用于返回 DataFrame 中每個元素的絕對值DataFrame.all([axis, bool_only, skipna])用于判斷 DataFrame 中是否所有元素在指定軸上都為 TrueDataFrame.any(*[, axis, bool_only, skipna])用于判斷…

C語言之旅5---分支與循環【2】

&#x1f4ab;只有認知的突破&#x1f4ab;才來帶來真正的成長&#x1f4ab;編程技術的學習&#x1f4ab;沒有捷徑&#x1f4ab;一起加油&#x1f4ab; &#x1f341;感謝各位的觀看&#x1f341;歡迎大家留言&#x1f341;咱們一起加油&#x1f341;努力成為更好的自己&#x…

docker大鏡像優化實戰

在 Docker 鏡像優化方面&#xff0c;有許多實戰技巧可以顯著減小鏡像體積、提高構建效率和運行時性能。以下是一些實用的優化策略和具體操作方法&#xff1a; 1. 選擇合適的基礎鏡像 策略 使用 Alpine 版本&#xff1a;Alpine 鏡像通常只有 5-10MB&#xff0c;比 Ubuntu/Deb…

Java面試終極篇:Sentinel+Seata+Kafka Streams高并發架構實戰

面試官&#xff1a;張總&#xff08;嚴肅臉&#xff09; 程序員&#xff1a;小王&#xff08;緊張冒冷汗&#xff09; 第一輪&#xff1a;分布式基礎 張總&#xff1a;說說Spring Cloud Alibaba的Sentinel和Nacos的區別&#xff1f; 小王&#xff1a;&#xff08;結巴&#…