大致自定義文件I/O庫函數的實現詳解(了解即可)

目錄

一、mystdio.h

代碼思路分析

二、mystdio.c

1. 輔助函數?BuyFile

2. 文件打開函數?MyFopen

3. 文件關閉函數?MyFclose

4. 數據寫入函數?MyFwrite

1、memcpy(file->outbuffer + file->bufferlen, str, len);

2、按位與(&)運算的作用

運算規則

5. 緩沖區刷新函數?MyFFlush

1、緩沖區空檢查

2、數據寫入系統調用

3、數據同步到磁盤

4、重置緩沖區

三、Makefile

四、Makefile-st


一、mystdio.h

#pragma once
// 這是一個預處理指令,確保頭文件在編譯時只被包含一次,防止重復包含#include <stdio.h>
// 包含標準輸入輸出庫,提供基本的文件操作函數原型#define MAX 1024
// 定義輸出緩沖區的大小為1024字節// 定義刷新模式的標志位
#define NONE_FLUSH (1<<0)   // 不自動刷新
#define LINE_FLUSH (1<<1)   // 行緩沖模式(遇到換行符時刷新)
#define FULL_FLUSH (1<<2)   // 全緩沖模式(緩沖區滿時刷新)// 自定義文件結構體,模擬標準庫的FILE結構
typedef struct IO_FILE
{int fileno;             // 文件描述符(底層操作系統文件標識)int flag;               // 文件打開模式標志char outbuffer[MAX];    // 輸出緩沖區int bufferlen;          // 當前緩沖區中的數據長度int flush_method;       // 緩沖刷新策略(NONE/LINE/FULL)
}MyFile;// 函數聲明/*** @brief 打開文件并初始化MyFile結構體* @param path 文件路徑* @param mode 打開模式("r"/"w"/"a"等)* @return 成功返回MyFile指針,失敗返回NULL*/
MyFile *MyFopen(const char *path, const char *mode);/*** @brief 關閉文件并釋放資源* @param fp MyFile指針*/
void MyFclose(MyFile *fp);/*** @brief 向文件寫入數據* @param fp MyFile指針* @param str 要寫入的數據指針* @param len 要寫入的數據長度* @return 成功寫入的字節數*/
int MyFwrite(MyFile *fp, void *str, int len);/*** @brief 手動刷新緩沖區* @param fp MyFile指針*/
void MyFFlush(MyFile *fp);

代碼思路分析

這段代碼實現了一個簡單的文件I/O封裝庫,模擬了標準C庫中的文件操作函數。主要特點包括:

  1. 緩沖機制

    • 使用outbuffer作為輸出緩沖區,減少直接系統調用的次數

    • 支持三種緩沖策略:無緩沖、行緩沖和全緩沖

  2. 結構設計

    • MyFile結構體封裝了文件描述符、緩沖區及狀態信息

    • 類似于標準庫的FILE結構,但更簡化

  3. 功能模擬

    • MyFopen:模擬fopen,打開文件并初始化結構體

    • MyFwrite:模擬fwrite,寫入數據到緩沖區

    • MyFFlush:模擬fflush,強制刷新緩沖區

    • MyFclose:模擬fclose,關閉文件并釋放資源

  4. 緩沖策略

    • NONE_FLUSH:每次寫入都立即刷新(無緩沖)

    • LINE_FLUSH:遇到換行符時刷新(行緩沖)

    • FULL_FLUSH:緩沖區滿時刷新(全緩沖)


二、mystdio.c

#include "mystdio.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>static MyFile *BuyFile(int fd, int flag)
{MyFile *f = (MyFile*)malloc(sizeof(MyFile));if(f == NULL) return NULL;f->bufferlen = 0;f->fileno = fd;f->flag = flag;f->flush_method = LINE_FLUSH;memset(f->outbuffer, 0, sizeof(f->outbuffer));return f;
}MyFile *MyFopen(const char *path, const char *mode)
{int fd = -1;int flag = 0;if(strcmp(mode, "w") == 0){flag = O_CREAT | O_WRONLY | O_TRUNC;fd = open(path, flag, 0666);}else if(strcmp(mode, "a") == 0){flag = O_CREAT | O_WRONLY | O_APPEND;fd = open(path, flag, 0666);}else if(strcmp(mode, "r") == 0){flag = O_RDWR;fd = open(path, flag);}else{//TODO}if(fd < 0) return NULL;return BuyFile(fd, flag);
}
void MyFclose(MyFile *file)
{if(file->fileno < 0) return;MyFFlush(file);close(file->fileno);free(file);
}
int MyFwrite(MyFile *file, void *str, int len)
{// 1. 拷貝memcpy(file->outbuffer+file->bufferlen, str, len);file->bufferlen += len;// 2. 嘗試判斷是否滿足刷新條件!if((file->flush_method & LINE_FLUSH) && file->outbuffer[file->bufferlen-1] == '\n'){MyFFlush(file);}return 0;
}
void MyFFlush(MyFile *file)
{if(file->bufferlen <= 0) return;// 把數據從用戶拷貝到內核文件緩沖區中int n = write(file->fileno, file->outbuffer, file->bufferlen);(void)n;fsync(file->fileno);file->bufferlen = 0;}

1. 輔助函數?BuyFile

BuyFile?函數在這個自定義 I/O 庫中模擬的是標準 C 庫(如 glibc)中?FILE?結構體的初始化過程。

/*** @brief 分配并初始化MyFile結構體* @param fd 文件描述符* @param flag 文件打開標志* @return 成功返回MyFile指針,失敗返回NULL*/
static MyFile *BuyFile(int fd, int flag)
{// 分配MyFile結構體內存MyFile *f = (MyFile*)malloc(sizeof(MyFile));if(f == NULL) return NULL;// 初始化結構體成員f->bufferlen = 0;           // 緩沖區初始為空f->fileno = fd;             // 設置文件描述符f->flag = flag;             // 設置文件打開標志f->flush_method = LINE_FLUSH; // 默認使用行緩沖模式memset(f->outbuffer, 0, sizeof(f->outbuffer)); // 清空緩沖區return f;
}

功能分析

  • 這是一個靜態輔助函數,用于創建和初始化MyFile結構體

  • 設置默認緩沖策略為LINE_FLUSH(行緩沖)

  • 清空輸出緩沖區,確保初始狀態干凈

2. 文件打開函數?MyFopen

/*** @brief 打開文件并初始化MyFile對象* @param path 文件路徑* @param mode 打開模式("w"/"a"/"r")* @return 成功返回MyFile指針,失敗返回NULL*/
MyFile *MyFopen(const char *path, const char *mode)
{int fd = -1;int flag = 0;// 根據模式設置打開標志if(strcmp(mode, "w") == 0)       // 寫模式{flag = O_CREAT | O_WRONLY | O_TRUNC; // 創建文件/只寫/清空內容fd = open(path, flag, 0666);  // 設置文件權限為rw-rw-rw-}else if(strcmp(mode, "a") == 0)  // 追加模式{flag = O_CREAT | O_WRONLY | O_APPEND; // 創建文件/只寫/追加fd = open(path, flag, 0666);}else if(strcmp(mode, "r") == 0)  // 讀模式{flag = O_RDWR;               // 讀寫模式fd = open(path, flag);}else{//TODO: 可以添加其他模式支持}// 檢查文件是否成功打開if(fd < 0) return NULL;// 創建并初始化MyFile對象return BuyFile(fd, flag);
}

功能分析

  • 支持三種基本文件模式:寫("w")、追加("a")和讀("r")

  • 使用系統調用open打開文件,獲取文件描述符

  • 根據模式設置不同的打開標志:

    • "w"模式會清空文件內容

    • "a"模式會在文件末尾追加

    • "r"模式允許讀寫

  • 最終調用BuyFile創建并初始化MyFile對象

3. 文件關閉函數?MyFclose

/*** @brief 關閉文件并釋放資源* @param file MyFile對象指針*/
void MyFclose(MyFile *file)
{if(file->fileno < 0) return;  // 檢查文件描述符是否有效MyFFlush(file);      // 確保緩沖區數據寫入文件close(file->fileno); // 關閉文件描述符free(file);          // 釋放MyFile結構體內存
}

功能分析

  • 首先刷新緩沖區,確保所有數據寫入文件

  • 關閉底層文件描述符

  • 釋放MyFile結構體內存

  • 包含安全檢查,防止無效文件描述符

4. 數據寫入函數?MyFwrite

/*** @brief 向文件寫入數據* @param file MyFile對象指針* @param str 要寫入的數據指針* @param len 要寫入的數據長度* @return 總是返回0(實際實現可能需要改進)*/
int MyFwrite(MyFile *file, void *str, int len)
{// 1. 將數據拷貝到緩沖區memcpy(file->outbuffer + file->bufferlen, str, len);file->bufferlen += len;  // 更新緩沖區長度// 2. 檢查是否滿足刷新條件if((file->flush_method & LINE_FLUSH) &&      // 如果是行緩沖模式file->outbuffer[file->bufferlen-1] == '\n') // 且最后一個字符是換行符{MyFFlush(file);  // 刷新緩沖區}return 0;  // 注意:實際應該返回寫入的字節數,這里需要改進
}

功能分析

1、memcpy(file->outbuffer + file->bufferlen, str, len);

memcpy(file->outbuffer + file->bufferlen, str, len);
  • file->outbuffer:指向文件輸出緩沖區的起始地址。

  • file->bufferlen:當前緩沖區中已存儲的數據長度(單位:字節)。

  • file->outbuffer + file->bufferlen:計算緩沖區中下一個空閑位置的地址(即已存數據的末尾)。

  • str:要寫入的數據源地址(用戶傳入的字符串或二進制數據)。

  • len:要拷貝的數據長度(單位:字節)。

  • memcpy:標準內存拷貝函數,將?str?的?len?字節數據復制到目標地址。

2、按位與(&)運算的作用

  • LINE_FLUSH?的定義(在頭文件中):

    #define LINE_FLUSH (1 << 1)  // 即二進制 0b10(十進制 2)
  • file->flush_method?是一個整型變量,存儲當前的緩沖模式(可能是?NONE_FLUSHLINE_FLUSH?或?FULL_FLUSH?的組合)。

運算規則

  • 按位與?&?會對兩個數的?二進制位?逐位比較:

    • 如果某一位在兩個數中都是?1,結果的該位才是?1,否則是?0

  • 判斷邏輯

    • 如果?file->flush_method?包含?LINE_FLUSH,則?file->flush_method & LINE_FLUSH?結果非零(true

    • 如果不包含,則結果為?0false)。

  • 將數據直接拷貝到輸出緩沖區

  • 實現了行緩沖邏輯:當檢測到換行符時自動刷新

  • 當前實現總是返回0,實際應該返回寫入的字節數

  • 缺少緩沖區滿時的處理邏輯(全緩沖模式)

5. 緩沖區刷新函數?MyFFlush

/*** @brief 刷新緩沖區,將數據寫入文件* @param file MyFile對象指針*/
void MyFFlush(MyFile *file)
{if(file->bufferlen <= 0) return;  // 緩沖區為空則直接返回// 將數據從用戶緩沖區寫入內核緩沖區int n = write(file->fileno, file->outbuffer, file->bufferlen);(void)n;  // 忽略返回值(實際實現應該檢查)fsync(file->fileno);  // 確保數據寫入磁盤file->bufferlen = 0;  // 重置緩沖區長度
}

功能分析

  • 將緩沖區內容寫入底層文件描述符

  • 使用fsync確保數據持久化到磁盤

  • 重置緩沖區長度為0

  • 當前實現忽略了write的返回值,實際應該處理寫入錯誤情況

1、緩沖區空檢查

if(file->bufferlen <= 0) return;
  • 作用:如果緩沖區中沒有數據,直接返回

  • 優化意義:避免不必要的系統調用

  • 潛在問題:沒有檢查?file?是否為 NULL(健壯性考慮)

2、數據寫入系統調用

int n = write(file->fileno, file->outbuffer, file->bufferlen);
(void)n;  // 忽略返回值
  • write?系統調用

    • 參數1:文件描述符

    • 參數2:要寫入的數據緩沖區

    • 參數3:要寫入的字節數

    • 返回值:實際寫入的字節數(可能小于請求的字節數)

  • 問題

    • 完全忽略了?write?的返回值,可能導致部分寫入未被處理

    • 沒有錯誤處理(如?write?返回 -1 表示出錯)

3、數據同步到磁盤

fsync(file->fileno);
  • fsync?系統調用

    • 強制將內核緩沖區中的數據寫入物理磁盤

    • 確保數據持久化,不會因系統崩潰而丟失

  • 性能影響

    • 這是一個昂貴的操作,會顯著降低 I/O 性能

    • 通常只在需要強一致性保證時使用

4、重置緩沖區

file->bufferlen = 0;
  • 作用:將緩沖區長度重置為0,表示緩沖區已空

  • 實現方式

    • 只是簡單地重置長度計數器

    • 沒有清空緩沖區內容(可能的安全問題)


三、Makefile

# 生成動態鏈接庫 libmyc.so,依賴 mystdio.o 和 mystring.o 兩個目標文件
# $@ 表示目標文件(libmyc.so),$^ 表示所有依賴文件
libmyc.so: mystdio.o mystring.ogcc -shared -o $@ $^# 編譯 mystdio.c 生成位置無關代碼(PIC)的目標文件
# -fPIC 選項是生成動態庫所必需的
# $< 表示第一個依賴文件(mystdio.c)
mystdio.o: mystdio.cgcc -fPIC -c $<# 編譯 mystring.c 生成位置無關代碼(PIC)的目標文件
mystring.o: mystring.cgcc -fPIC -c $<# 偽目標:打包輸出庫文件
.PHONY: output
output:# 創建發布目錄結構mkdir -p lib/include    # 存放頭文件的目錄mkdir -p lib/mylib      # 存放動態庫的目錄# 拷貝所有頭文件到include目錄cp -f *.h lib/include# 拷貝動態庫文件到mylib目錄cp -f *.so lib/mylib# 將整個lib目錄打包壓縮tar czf lib.tgz lib# 偽目標:清理生成的文件
.PHONY: clean
clean:# 刪除所有目標文件、動態庫文件和生成的目錄rm -rf *.o libmyc.so lib lib.tgz

四、Makefile-st

# 生成靜態鏈接庫 libmyc.a,依賴 mystdio.o 和 mystring.o 兩個目標文件
# 注意:與動態庫(.so)不同,靜態庫(.a)在編譯時會被完整嵌入可執行文件
# $@ 表示目標文件(libmyc.a),$^ 表示所有依賴文件
libmyc.a: mystdio.o mystring.o# 使用 ar 命令打包目標文件生成靜態庫# -r 替換現有文件 -c 創建新庫ar -rc $@ $^# 編譯 mystdio.c 生成目標文件
# 注意:靜態庫不需要 -fPIC 位置無關代碼選項(與動態庫不同)
# $< 表示第一個依賴文件(mystdio.c)
mystdio.o: mystdio.cgcc -c $<# 編譯 mystring.c 生成目標文件
mystring.o: mystring.cgcc -c $<# 偽目標:打包發布靜態庫文件
.PHONY: output
output:# 創建標準的庫發布目錄結構mkdir -p lib/include    # 存放頭文件(.h)的目錄mkdir -p lib/mylib      # 存放靜態庫文件(.a)的目錄# 拷貝所有頭文件到include目錄cp -f *.h lib/include# 拷貝靜態庫文件到mylib目錄(注意這里是.a文件而非.so)cp -f *.a lib/mylib# 將整個lib目錄打包壓縮,便于分發tar czf lib.tgz lib# 偽目標:清理生成的文件
.PHONY: clean
clean:# 刪除編譯過程中生成的所有中間文件# 包括:目標文件(.o)、靜態庫文件(.a)、打包目錄和壓縮包rm -rf *.o libmyc.a lib lib.tgz

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

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

相關文章

Zipformer

Zipformer首先&#xff0c;Conv-Embed 將輸入的 100Hz 的聲學特征下采樣為 50 Hz 的特征序列&#xff1b;然后&#xff0c;由 6 個連續的 encoder stack 分別在 50Hz、25Hz、12.5Hz、6.25Hz、12.5Hz 和 25Hz 的采樣率下進行時域建模。除了第一個 stack 外&#xff0c;其他的 st…

SpringMVC快速入門之請求與響應

SpringMVC快速入門之請求與響應一、請求處理&#xff1a;獲取請求參數1.1 普通參數獲取&#xff08;RequestParam&#xff09;1.1.1 基礎用法1.1.2 可選參數與默認值1.2 路徑變量&#xff08;PathVariable&#xff09;1.3 表單數據綁定到對象1.3.1 定義實體類1.3.2 綁定對象參數…

【Mysql】 Mysql zip解壓版 Win11 安裝備忘

1. 官網 MySQL :: MySQL Community Downloads 選擇 MySQL Community Server 選擇Archives 選擇 8.0版本 MySQL :: Download MySQL Community Server (Archived Versions) 1. 普通版本&#xff08;推薦&#xff09; 名稱&#xff1a;Windows (x86, 64-bit), ZIP Archive 文件…

Web3面試題

1.在使用 Ethers.js 對接 MetaMask 錢包時&#xff0c;如何檢測用戶賬戶切換的情況&#xff1f;請簡述實現思路。 答案&#xff1a;可通過監聽accountsChanged事件來檢測。當用戶切換賬戶時&#xff0c;MetaMask 會觸發該事件&#xff0c;在事件回調函數中可獲取新的賬戶地址&…

uni-app動態獲取屏幕邊界到安全區域距離的完整教程

目錄 一、什么是安全區域&#xff1f; 二、獲取安全區域距離的核心方法 三、JavaScript動態獲取安全區域距離 1. 核心API 2. 完整代碼示例 3. 關鍵點說明 四、CSS環境變量適配安全區域 1. 使用 env() 和 constant() 3. 注意事項 五、不同平臺的適配策略 1. H5 端 2…

ZKmall開源商城微服務架構實戰:Java 商城系統的模塊化拆分與通信之道

在電商業務高速增長的今天&#xff0c;傳統單體商城系統越來越力不從心 —— 代碼堆成一團、改一點牽一片、想加功能得大動干戈&#xff0c;根本扛不住高并發、多場景的業務需求。微服務架構卻能破這個局&#xff1a;把系統拆成一個個能獨立部署的小服務&#xff0c;每個服務專…

ROS 與 Ubuntu 版本的對應關系

ROS 作為一套用于構建機器人應用的開源框架&#xff0c;其開發和運行高度依賴 Ubuntu 等 Linux 發行版&#xff0c;尤其是 Ubuntu 因其廣泛的兼容性和社區支持&#xff0c;成為了 ROS 最主流的運行平臺。 一、ROS 與 Ubuntu 版本的對應關系&#xff08;截至 2025 年&#xff0c…

GPT-4o mini TTS:領先的文本轉語音技術

什么是 GPT-4o mini TTS&#xff1f; GPT-4o mini TTS 是 OpenAI 推出的全新一代文本轉語音&#xff08;TTS&#xff09;技術&#xff0c;能夠以自然、流暢的方式將普通文本轉換為語音。依托先進的神經網絡架構&#xff0c;GPT-4o mini TTS 在語音合成中避免了傳統 TTS 的生硬…

Git下載全攻略

目標讀者初學者或有經驗的開發者不同操作系統用戶&#xff08;Windows、macOS、Linux&#xff09;下載前的準備確認系統版本和位數&#xff08;32-bit/64-bit&#xff09;檢查網絡環境是否穩定確保有足夠的磁盤空間Windows系統下載Git訪問Git官方網站&#xff08;https://git-s…

ADAS域控軟件架構-網絡管理狀態與喚醒機制

1. 狀態介紹: Sleep Mode:總線睡眠模式,控制器不發送應用報文和網絡管理報文。 Pre-Sleep Mode:準備總線睡眠模式,控制器不發送應用報文和網絡管理報文。 Ready Sleep Mode:就緒睡眠模式,系統發送應用報文但是不發送網絡管理報文。 Normal Operation mode:正常工作模式…

pytest簡單使用和生成測試報告

目錄 1. 基本使用 1--安裝 2--pytest書寫規則 3--為pycharm設置 以 pytest的方式運行 4--setup和teardown 5--setup_class和teardown 2. pytest生成測試報告 基本使用 安裝 pytest文檔地址 pytest documentation pip install pytest點擊pycharm左邊的控制臺按鈕 輸入pip inst…

Spring Boot 第一天知識匯總

一、Spring Boot 是什么&#xff1f;簡單說&#xff0c;Spring Boot 是簡化 Spring 應用開發的框架 —— 它整合了整個 Spring 技術棧&#xff0c;提供了 “一站式” J2EE 開發解決方案。核心優點&#xff1a;快速創建獨立運行的 Spring 項目&#xff0c;無需繁瑣配置&#xff…

MySql主從部署

MySql主從部署 1、操作環境 硬件環境&#xff1a;香橙派5 aarch64架構 軟件環境&#xff1a;Ubuntu 22.04.3 LTS 軟件版本&#xff1a;mysql-8.0.42 操作方式&#xff1a;mysql_1,mysql_2容器 主節點&#xff1a;mysql_1 啟動命令&#xff1a;docker run --name mysql_master \…

Redis——Redis進階命令集詳解(下)

本文詳細介紹了Redis一些復雜命令的使用&#xff0c;包括Redis事務相關命令&#xff0c;如MULTI、EXEC、DISCARD 和 WATCH ,發布訂閱操作命令&#xff0c;如PUBLISH 、SUBSCRIBE 、PSUBSCRIBE ,BitMap操作命令&#xff0c;如SETBIT、GETBIT、BITCOUNT、BITOP&#xff0c;HyperL…

C#使用socket報錯 System.Net.Sockets.SocketException:“在其上下文中,該請求的地址無效。

bind: 在其上下文中&#xff0c;該請求的地址無效。問題定位 程序中運行socket服務端程序時&#xff0c;綁定的IP地址無效&#xff0c;即請求的IP地址在你的機子上找不到。原因有以下幾種可能&#xff1a; 1&#xff09;server端綁定的IP地址不是本機的IP地址。 2&#xff09;之…

計算機底層入門 05 匯編學習環境通用寄存器內存

2.3 匯編學習環境我們通過上一章筆記&#xff0c;得知 計算機好像 只會通過位運算 進行 數字的加法。 而機器語言的魅力就是 位運算&#xff0c;解析規則。它們也都是通過 電路 來進行實現的。這就是 計算機最底層的本質了&#xff01;&#xff01;&#xff01; 匯編語言 所謂的…

Java學習---Spring及其衍生(上)

在 Java 開發領域&#xff0c;Spring 生態占據著舉足輕重的地位。從最初的 Spring 框架到后來的 SpringBoot、SpringMVC 以及 SpringCloud&#xff0c;每一個組件都在不同的場景下發揮著重要作用。本文將深入探討這幾個核心組件&#xff0c;包括它們的定義、原理、作用、優缺點…

LVGL應用和部署(個人開發嵌入式linux產品)

【 聲明&#xff1a;版權所有&#xff0c;歡迎轉載&#xff0c;請勿用于商業用途。 聯系信箱&#xff1a;feixiaoxing 163.com】隨著經濟越來越走向常態化發展&#xff0c;將來的公司基本是兩個趨勢&#xff0c;一個是公司越做越大&#xff0c;越來越趨向于壟斷&#xff1b;另外…

CPU,減少晶體管翻轉次數的編碼

背景 以4比特為單位&#xff0c;共16個數。仔細思考狀態轉換過程中的晶體管翻轉次數。 0000 0001&#xff0c;1 0010&#xff0c;2 0011&#xff0c;1 0100&#xff0c;3 0101&#xff0c;1 0110&#xff0c;2 0111&#xff0c;1 1000&#xff0c;4 1001&#xff0c;1 1010&…

LLM 中的 溫度怎么控制隨機性的?

LLM 中的 溫度怎么控制隨機性的? 在LLM的解碼過程中,溫度(Temperature)通過調整token概率分布的“陡峭程度”來控制隨機性:溫度越低,概率分布越陡峭(高概率token的優勢越明顯),隨機性越低;溫度越高,分布越平緩(高低概率token的差異被縮小),隨機性越高。 溫度,…