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

C語言 free() 如何知道釋放多少記憶體?揭秘分配器元數據機制

     編輯:C 語言問答

核心結論

free() 並不依賴指標類型(int\*、char\* 等)決定大小,而是從記憶體塊頭部的隱藏元數據中讀取塊大小。

記憶體分配器內部結構示意

+------------------+------------------+------------------+
| 分配器頭部元數據  | 用戶可用區域      | 可能的對齊填充    |
| (size, flags...) | ← malloc 返回指標 |                  |
+------------------+------------------+------------------+
      ↑
   實際起始地址 (malloc 內部記錄)
  • 用戶拿到的指標 p 指向 **用戶可用區域起始**。
  • 分配器內部將 p - header_size 視為塊真實起始,頭部存儲 **塊大小、魔數、空閒標記、前後塊指標** 等。
  • free(p) 時,分配器向前偏移找到頭部,**讀取 size 字段**,據此將塊標記為空閒並合併相鄰空閒塊。

為什麼指標類型無關?

int *p1 = malloc(4);   // 分配 4 字節(或更多對齊)
char *p2 = malloc(4);  // 同樣分配 4 字節
free(p1); free(p2);    // 兩者釋放相同大小,因頭部記錄的均為實際分配大小

sizeof(*p1) 編譯期僅知為 4,但 malloc 運行期可能分配 16/32 字節(對齊、最小塊限制)。**釋放大小以運行期分配器記錄為準,而非編譯期類型。**

主流分配器實現差異

分配器頭部大小特點
glibc ptmalloc8/16 字節 (64/32位)多執行緒 arena、fastbin、tcache、大塊 mmap
Windows Heap8 字節 + 可選前端 LFH (Low Fragmentation Heap)、後端段管理
dlmalloc (嵌入式常用)4/8 字節邊界標記、雙向循環空閒鏈表

實驗驗證:偷看元數據(僅教學)

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *p = malloc(100);
    printf("User ptr: %p\n", (void*)p);
    // 假設 64 位 glibc,頭部在 p - 8 字節 (size_t)
    size_t *header = (size_t *)((char *)p - sizeof(size_t));
    printf("Recorded size (with flags): %zu\n", *header);
    free(p);
    return 0;
}

注意:直接讀寫元數據屬於未定義行為,僅供理解原理,生產代碼嚴禁。

工程啟示

  1. 嚴禁 用 sizeof(*ptr) 推斷 malloc 大小——錯誤且危險。
  2. **自己記錄大小**:結構體含 size_t len 字段,或用 malloc_usable_size(p) (glibc) / _msize(p) (Windows) 獲取實際可用大小。
  3. **配對原則**:每個 malloc/calloc/realloc 必須對應一次 free,避免洩漏/重複釋放。
  4. **釋放後置 NULL**:free(p); p = NULL; 防止野指標二次釋放。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved