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

C 語言內存管理精華:sizeof、對齊、malloc/calloc/realloc/alloca 核心機制與避坑指南

     編輯:關於C語言

內存管理是 C 語言的核心竞争力,也是最大坑源。本文系統整理 sizeof 語義、結構體對齊規則、三大分配區(靜態/棧/堆)、malloc/calloc/realloc/alloca 區別與陷阱、內存洩漏成因與檢測工具,構建完整知識體系。

一、sizeof:編譯期常量,非函數

  • 返回類型:size_t(無符號整數)。
  • 操作數:類型名(需括號)或表達式(不求值,僅推導類型)。
  • 不適用:函數類型、不完全類型(未知大小陣列、前向聲明結構體、void)、位域。

常見類型大小(x86-64 Linux/GCC 典型值)

類型大小備註
char / signed char / unsigned char1定義為 1 字節
short2
int4
long8Windows 為 4
long long8
float4
double8
指針864 位系統

二、結構體對齊與填充


struct MyStruct {
    char  a;     // offset 0, size 1
    // 7 bytes padding
    double b;    // offset 8 (align 8), size 8
    int   c;     // offset 16 (align 4), size 4
    // 4 bytes padding (struct align = max member align = 8)
};               // total = 24
  • 每個成員按 alignof(type) 對齊,編譯器自動插入填充字節。
  • 結構體整體大小為最大成員對齊數的倍數(尾部填充)。
  • #pragma pack(n) 可壓縮對齊,但可能導致非對齊訪問性能下降或硬件異常(ARM)。
  • offsetof 宏獲取成員偏移,_Alignof (C11) 獲取對齊要求。

三、三大內存區與分配函數

區域分配方式生命週期典型大小速度用例
靜態/全局編譯期固定程序全周期受限於數據段最快全局變量、static 變量、字串字面量
棧編譯器自動(alloca 手動)函數調用周期~MB 級(線程棧)極快(指針移動)局部變量、臨時緩衝、遞歸
堆手動 malloc/free程序員控制虛擬地址空間較慢(系統調用、鎖、碎片整理)動態數據結構、大塊緩衝、跨函數生命週期

四、堆分配函數族詳細對比


void *malloc(size_t size);           // 未初始化,內容不定
void *calloc(size_t nmemb, size_t size); // 全 0 初始化(位模式全 0)
void *realloc(void *ptr, size_t size);   // 調整大小,可能移動內存
void free(void *ptr);                // 釋放,ptr 必須來自 malloc/calloc/realloc
void *alloca(size_t size);           // 棧上分配,函數返回自動釋放(非標準,慎用)

關鍵區別

  • malloc vs calloc:calloc 零初始化,對指針類型不保證產生 NULL(位模式全 0 ≠ 空指針表示),對浮點數不保證產生 0.0。malloc 需手動 memset。
  • calloc 內部優化:OS 可直接映射零頁,大塊分配比 malloc+memset 快。
  • realloc 遷移:可能返回新地址,舊指針失效,必須用返回值更新指針。
  • alloca 危險:棧溢出無保護、不支持可變長度數組(C99 VLA 已棄用)、異常處理/長跳轉時不釋放。

五、分配器內部機制簡析

  • 用戶調用 malloc → 分配器從空閒鏈表/紅黑樹尋找合適塊 → 分割/合並 → 返回用戶指針(通常指向用戶數據區,元數據在前)。
  • 空閒內存不足 → 調用 brk/mmap 向 OS 申請大塊(批發)→ 再切分(零售)。
  • free 不立即歸還 OS,放入空閒池供下次複用(緩衝機制)。
  • 內存洩漏:malloc 無對應 free,長期運行進程可用內存單調下降,最終 OOM。

六、避坑檢查表

陷阱後果對策
malloc 未檢查 NULL解引用空指針崩潰永遠檢查返回值
free 後繼續使用指針Use-after-free,未定義行為free 後置 NULL
重複 free堆損壞,崩潰free 後置 NULL,邏輯保證單次
釋放非堆指針堆損壞只 free malloc 系返回指針
柔性數組結構體 malloc 尺寸錯誤越界寫入malloc(sizeof(Struct) + n * sizeof(Elem))
realloc 失敗未保存舊指針內存洩漏void *tmp = realloc(p, sz); if (!tmp) { /* handle */ } p = tmp;

七、檢測工具

  • Valgrind Memcheck:valgrind --leak-check=full ./prog,檢測洩漏、越界、使用未初始化值。
  • AddressSanitizer (ASan):gcc -fsanitize=address -g,編譯期插桿,運行時檢測,速度快於 Valgrind。
  • Malloc 棧保護:MALLOC_CHECK_=3 環境變量啟用 glibc 內部一致性檢查。

總結

掌握 sizeof/對齊、三大區特性、分配函數語義、分配器工作原理、常見陷阱與檢測工具,是寫出高效、穩健、無洩漏 C 代碼的必經之路。原則:能靜態/棧分配就不上堆;上堆必成對 free;大塊優先 calloc;工具常掛 CI。

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