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

C 語言指標陷阱:未配置記憶體直接解參考 —— 從崩潰到正確的 malloc/free 實踐

     編輯:關於C

現場還原:一段「看起來沒錯」但會崩潰的程式碼

#include 
#include 

struct Person { int age; char name; };
struct Student { struct Person abc; int id; };

struct Student *f1;  // 全域指標,未初始化 (靜態儲存區預設為 NULL)

int main(void) {
    f1->id = 20;              // ❌ 段錯誤:解參考野指標/NULL
    printf("%d\n", f1->id);
    f1->abc.age = 20;         // 同樣崩潰
    printf("%d\n", f1->abc.age);
    return 0;
}

錯誤現象:編譯通過,執行時直接崩潰 (Segmentation Fault / Access Violation)。初學者常困惑:「語法都對啊,為什麼不行?」

根因分析:指標 ≠ 物件,指標需要「指向有效記憶體」

  • struct Student *f1; 只是宣告一個**指標變數** (8 位元組/64位系統),儲存的是**位址**。
  • 未初始化的全域指標預設為 NULL (0 位址),區域指標則是**不定值 (垃圾位址)**。
  • f1->id 等同 (*f1).id,嘗試存取位址 0 (或垃圾位址) 的記憶體 → **作業系統保護機制攔截,進程終止**。

正確做法:malloc 配置、使用、釋放 三步驟

#include 
#include 

struct Person { int age; char name; };
struct Student { struct Person abc; int id; };

int main(void) {
    // 1️⃣ 配置記憶體:向堆疊申請 sizeof(struct Student) 位元組
    struct Student *f1 = malloc(sizeof(struct Student));
    if (!f1) { perror("malloc 失敗"); return 1; }  // 必須檢查 NULL

    // 2️⃣ 使用記憶體:現在 f1 指向有效可寫入區域
    f1->id = 20;
    printf("%d\n", f1->id);        // 20
    f1->abc.age = 20;
    printf("%d\n", f1->abc.age);   // 20

    // 3️⃣ 釋放記憶體:歸還給堆疊,避免記憶體洩漏
    free(f1);
    f1 = NULL;  // 防懸空指標 (好習慣)

    return 0;
}

記憶體圖解

執行 malloc 後:

堆疊 (Heap)                    棧 (Stack - main)
+---------------------+        +---------------------+
| struct Student      | ←────── | f1 (指標) 0x7f...   |
|   id: 20            |         |                     |
|   abc.age: 20       |         +---------------------+
|   abc.name: ?       |
+---------------------+

常見錯誤模式對照表

錯誤程式碼問題修正
int *p; *p = 10;野指標解參考int *p = malloc(sizeof(int));
char *s; scanf("%s", s);未配置緩衝區char s[100]; 或 malloc(100)
Node *head; head->next = NULL;鏈表頭節點未 mallochead = malloc(sizeof(Node));
free(p); p->val = 0;釋放後使用 (UAF)free(p); p = NULL;
p = malloc(10); p = malloc(20);記憶體洩漏 (第一塊失聯)先 free(p) 再重新配置

進階:柔性陣列成員 (C99) 與結構體配置

若結構體包含柔性陣列,配置大小需加上陣列長度:

struct Buffer { size_t len; char data[]; };  // 柔性陣列

struct Buffer *buf = malloc(sizeof(struct Buffer) + 100);  // 額外 100 位元組
buf->len = 100;
// 使用 buf->data[0..99]
free(buf);

除錯技巧:工具輔助發現記憶體錯誤

  • Valgrind (Linux/macOS):valgrind --leak-check=full ./prog 偵測洩漏、非法存取、未初始化值
  • AddressSanitizer (GCC/Clang):編譯加 -fsanitize=address -g,執行時即時回報錯誤位置
  • Dr. Memory / Visual Studio 診斷工具 (Windows)

最佳實踐清單

  1. 宣告即初始化:Type *p = NULL; 或 Type *p = malloc(...);
  2. 檢查 malloc 回傳值:永遠假設配置可能失敗
  3. 配置釋放配對:每個 malloc/calloc/realloc 對應一個 free
  4. 釋放後歸零:free(p); p = NULL; 防止 Double Free / UAF
  5. 避免 gets、scanf("%s"):改用 fgets、scanf("%99s", buf) 指定寬度
  6. 善用靜態分析:clang --analyze、cppcheck、Coverity

總結

指標是 C 語言的靈魂,也是最大的坑源。**指標變數本身不包含資料,只包含位址;位址必須指向有效、已配置、未釋放的記憶體區域,解參考才安全**。養成「宣告→配置→檢查→使用→釋放→歸零」的肌肉記憶,能消除 99% 的指標相關崩潰。

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