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

C 語言動態記憶體管理深度解析:malloc/free/realloc 實戰與鏈表應用

     編輯:關於C語言

核心函式原型

#include <stdlib.h>

void *malloc(size_t size);           // 申請 size 位元組,未初始化
void *calloc(size_t nmemb, size_t size); // 申請 nmemb*size,歸零
void *realloc(void *ptr, size_t size);   // 調整既有區塊大小
void free(void *ptr);                // 釋放,ptr 需為 malloc 系回傳或 NULL

記憶體區域與指標角色

  • 堆區:`malloc` 族函式唯一來源,位址由執行期決定,無變數名、無型別。
  • 指標:唯一「把持」堆區塊的手柄,儲存於靜態區/棧區/其它堆區塊中。
  • 型別轉換:`malloc` 回傳 `void*`,C 語言隱性轉換為目標指標型別;C++ 需顯式 `static_cast`。

三大函式行為細節

函式行為常見誤區
`malloc(n)`成功返回對齊後位址,失敗返回 `NULL`;內容不定值以為會歸零;忘記檢查 `NULL`
`calloc(n, sz)`等價 `malloc(n*sz)` + `memset(0)`;溢位檢查更安全以為比 `malloc` 慢很多 (實際差異極小)
`realloc(p, n)`可能原地擴展、可能移動、可能縮小;`p==NULL` 等價 `malloc(n)`直接 `p = realloc(p, n)` 導致移動時舊指標遺失 → 記憶體洩漏

realloc 安全模式

void *tmp = realloc(ptr, new_size);
if (!tmp) { /* 處理失敗,ptr 仍有效,可繼續使用或釋放 */ }
ptr = tmp;  // 只有成功才更新指標

實戰範例:動態陣列結構體

typedef struct {
    int *data;
    size_t size;      // 當前元素數
    size_t capacity;  // 已分配容量
} DynArray;

bool dyn_init(DynArray *a) {
    a->capacity = 4;
    a->size = 0;
    a->data = malloc(a->capacity * sizeof(int));
    return a->data != NULL;
}

bool dyn_push(DynArray *a, int val) {
    if (a->size == a->capacity) {
        size_t new_cap = a->capacity * 2;
        int *tmp = realloc(a->data, new_cap * sizeof(int));
        if (!tmp) return false;
        a->data = tmp;
        a->capacity = new_cap;
    }
    a->data[a->size++] = val;
    return true;
}

void dyn_free(DynArray *a) {
    free(a->data);
    a->data = NULL;
    a->size = a->capacity = 0;
}

鏈表:動態記憶體的經典載體

typedef struct Node {
    int val;
    struct Node *next;
} Node;

Node *list_append(Node *head, int val) {
    Node *node = malloc(sizeof(Node));
    if (!node) return head;  // 失敗不修改原鏈表
    node->val = val;
    node->next = NULL;

    if (!head) return node;
    Node *cur = head;
    while (cur->next) cur = cur->next;
    cur->next = node;
    return head;
}

void list_free(Node *head) {
    while (head) {
        Node *tmp = head;
        head = head->next;
        free(tmp);
    }
}

常見錯誤與偵錯工具

錯誤類型現象偵測工具
記憶體洩漏長期運行記憶體單調上升Valgrind `--leak-check=full`、AddressSanitizer `-fsanitize=address`
重複釋放`free()` 兩次同一指標 → 崩潰/堆損壞ASan、Valgrind `invalid free`
釋放後使用讀寫已 `free` 區塊 → 未定義行為ASan `heap-use-after-free`
緩衝區溢位寫入超出 `malloc` 大小ASan `heap-buffer-overflow`、Electric Fence
未對齊存取某架構 (SPARC/ARM 舊) 存取非對齊位址崩潰編譯器警告、運行時 SIGBUS

最佳實踐清單

  1. 永遠檢查回傳值:`if (!p) handle_oom();`
  2. 配對原則:每個 `malloc`/`calloc`/`realloc` 成功必有對應 `free`,所有權清楚 (誰配置誰釋放,或文件化轉移)。
  3. 釋放歸零:`free(p); p = NULL;` 防止二次釋放/野指標。
  4. 柔性陣列成員 (C99):`struct { size_t len; char data[]; }` 單次分配承載結構體+變長資料,減少碎片、提升快取命中。
  5. 自定義分配器:嵌入式/高效能場景實作 Pool/Slab/Bump Allocator,統一介面 `void* alloc(size_t)` / `void dealloc(void*)`。
  6. 啟用強化模式:`MALLOC_CHECK_=3` (glibc)、`MallocScribble=1` (macOS)、`PageHeap` (Windows) 開發期捕獲堆損壞。

動態記憶體是 C 語言威力的來源,也是 Bug 溫床。掌握「malloc 族三大金剛、realloc 安全模式、鏈表實戰、ASan/Valgrind 雙重防線」,讓指標在堆區自由馳騁而不失控。

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