首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

C 語言記憶體錯誤深度剖析:fgetc 迴圈、指標運算與 fseek 邏輯陷阱

     編輯:程式設計綜合問答

錯誤現象

程式執行到 system("cls") 前崩潰,彈窗提示「記憶體無法為 read/write」,且伴隨 360 誤報、CMD 無法關閉等連鎖異常。核心原因在於 getTextFileContent 與 getFileSize 兩個函數的嚴重 Bug。

Bug 1:fseek 回傳值邏輯反轉

// 原碼 錯誤
long size = (fseek(f, 0L, SEEK_END) != 0) ? (ftell(f)) : -1;
// fseek 成功返回 0,失敗返回非零。
// 原碼在「成功時」執行 ftell,但條件寫成 != 0,導致成功時返回 -1,失敗時才取 ftell (未定義行為)。

修正:

long size = (fseek(f, 0L, SEEK_END) == 0) ? ftell(f) : -1;
// 或更清寫:
if (fseek(f, 0L, SEEK_END) != 0) return -1;
long size = ftell(f);
rewind(f);

Bug 2:指標算術與記憶體寫入越界

// 原碼片段
char *ptr = (char*)malloc(size + chLen);
char *block = ptr;
// ...
while((c = fgetc(fp)) != EOF) {
    ch = (char)c;
    memcpy(ptr, &ch, chLen);
    (int)ptr += chLen;  // 嚴重錯誤:將指標強轉為 int 再加法,64 位元系統截斷高 32 位,指標變野指標!
}
memcpy(ptr, &endC, chLen); // 寫入結束符時 ptr 已失效,導致堆損壞

修正: 直接使用指標算術 ptr += chLen; (指標自動按類型大小遞增,此處為 1),絕不可轉為整數運算。

Bug 3:main 函數中三元運算子導致釋放字串常量

// 原碼
text = (text == NULL) ? "" : text;
// 若 text 為 NULL,text 指向唯讀字串常量區 "",後續 free(text) 嘗試釋放常量區記憶體 → 崩潰。

修正: 直接判斷 if (text == NULL) text = ""; 但後續不可 free(text);或分配一份副本 strdup("")。最佳做法:if (!text) text = strdup(""); 並確保最後 free(text) 安全。

完整修正版 getTextFileContent

char* getTextFileContent(const char *path) {
    FILE *fp = fopen(path, "rb"); // 二進制模式避免換行轉換影響大小
    if (!fp) return NULL;
    
    if (fseek(fp, 0L, SEEK_END) != 0) { fclose(fp); return NULL; }
    long size = ftell(fp);
    rewind(fp);
    
    char *buf = (char*)malloc(size + 1);
    if (!buf) { fclose(fp); return NULL; }
    
    size_t read = fread(buf, 1, size, fp); // 一次性讀取,效率高且無指標運算風險
    buf[read] = '\0';
    fclose(fp);
    return buf;
}

其他隱患與建議

  • mStrCat 使用 memmove + strcat,若 str1 未以 \0 結尾會越界。建議改用 snprintf 或標準 strdup + strcat 並檢查長度。
  • getTD() 中 struct tm tmptm = {0,0,0,9,5,2015-1900}; 月份欄位 tm_mon 為 0-11,5 代表 6 月,正確;但 mktime 依賴本地時區,跨時區部署需注意。
  • 編譯器警告 -Wall -Wextra -Wpedantic 會捕捉指標轉 int、格式字串不符等多數問題,務必開啟並修復所有警告。

總結

這起崩潰是典型的 C 語言手動記憶體管理陷阱組合拳:fseek 邏輯反 → 分配錯誤大小 → 指標強轉 int 運算 → 寫入越界破壞堆元資料 → free 觸發崩潰。改用 fread 一次性讀取、指標原生算術、嚴格檢查分配/釋放配對,即可根治。

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