首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C 陷阱與缺陷:fread/fwrite 交替調用必須中間插 fseek

     編輯:關於C語言

現象:看似成功實則寫入失效

FILE* fp = fopen("test.txt", "r+");
char buf[256] = {0};
size_t n = fread(buf, 1, 10, fp);  // 讀 10 字節
// 處理 buf...
// fseek(fp, -10, SEEK_CUR);        // 關鍵行:被註釋掉
fwrite(buf, 1, n, fp);             // 返回 n,但文件未變!

現象:fread 和 fwrite 均返回成功,ferror 為 0,但打開文件發現內容未更新。

根本原因:C 標準 IO 流方向切換規則

C99 §7.19.5.3/6:在輸入操作後進行輸出操作,或輸出後進行輸入,必須中間插入文件定位操作(fseek/fsetpos/rewind),除非輸入操作遇到 EOF。

原因:FILE 結構體內維護單一緩衝區與狀態標誌(_IOREAD/_IOWRITE)。切換方向需沖刷緩衝區、重置指針、更新標誌,fseek 正是觸發此狀態機切換的標準動作。

正確代碼模式

// 讀 → 寫
fread(buf, 1, n, fp);
fseek(fp, -n, SEEK_CUR);  // 回到讀取起始位置
fwrite(buf, 1, n, fp);

// 寫 → 讀
fwrite(buf, 1, n, fp);
fseek(fp, -n, SEEK_CUR);  // 或 fflush(fp); fseek(fp, 0, SEEK_CUR);
fread(buf, 1, n, fp);

常見誤區

  • 以為 fflush 就夠了:fflush 只沖刷輸出緩衝區,不重置讀寫狀態標誌,後續 fread 仍會讀到舊緩衝區數據或失敗。
  • 以為 "r+" 默認同步:標準 IO 默認全緩衝(塊設備),不自動同步。
  • 只在舊編譯器/舊平台出現:現代 glibc/MSVC CRT 內部可能容錯,但**標準不保證**,移植時必現 Bug。

最佳實踐

  1. 嚴格遵守「讀寫切換必 fseek」,哪怕偏移量為 0(fseek(fp, 0, SEEK_CUR))。
  2. 單向流更安全:能用 "r" 或 "w"/"a" 就不用 "r+", "w+", "a+"。
  3. C++ 優先用 iostream:std::fstream 內部自動處理狀態切換,或用 std::filebuf 手動 pubseekoff。
  4. 二進制模式 "rb+"/"wb+":避免換行符轉換干擾位置計算。

延伸:C++ iostream 同類陷阱

std::fstream fs("test.bin", std::ios::in | std::ios::out | std::ios::binary);
fs.read(buf, 10);
// fs.seekg(-10, std::ios::cur); // 必須 seekg/seekp 同步雙向指針
fs.write(buf, 10);

std::fstream 維護獨立的 get pointer (gptr) 和 put pointer (pptr),切換讀寫必須同步兩者,seekg/seekp 即可。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved