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

C 語言指針陣列陷阱:函數返回區域陣列指針導致野指針

     編輯:程式設計綜合問答

代碼意圖:函數 `putin()` 讀取 5 個整數存入陣列並返回指針,`main()` 接收後打印。實際輸出亂碼/地址,核心原因是 **函數返回了區域變數(棧上陣列)的指針**,函數結束時棧幀銷毀,指針指向的記憶體已失效。

錯誤代碼分析

int *putin() {
    int array[5];           // ❌ 區域陣列,存放在棧上
    // ... 輸入邏輯 ...
    return array;           // 返回棧地址
}

int main() {
    int *p = putin();       // p 指向已銷毀的棧記憶體
    for (int k=0; k<5; k++, p++) printf("%d ", *p); // 未定義行為
}

三種標準修正方案

方案一:主調函數分配陣列,傳指針給被調函數填充(推薦)

void putin(int *arr, int n) { // 接收外部陣列指針
    printf("請輸入 %d 個數字: ", n);
    for (int i = 0; i < n; ++i) {
        scanf("%d", &arr[i]);
    }
}

int main() {
    int all[5];               // ✅ 在 main 棧框上,生命週期覆蓋整個 main
    putin(all, 5);            // 傳入首地址
    for (int k = 0; k < 5; ++k) printf("%d ", all[k]);
    return 0;
}

方案二:動態分配堆記憶體,調用者負責釋放

int *putin(int n) {
    int *arr = malloc(n * sizeof(int)); // ✅ 堆記憶體,函數結束不銷毀
    if (!arr) return NULL;
    printf("請輸入 %d 個數字: ", n);
    for (int i = 0; i < n; ++i) scanf("%d", &arr[i]);
    return arr; // 調用者必須 free()
}

int main() {
    int *p = putin(5);
    if (p) {
        for (int k = 0; k < 5; ++k) printf("%d ", p[k]);
        free(p); // ✅ 必須釋放
    }
    return 0;
}

方案三:靜態區域變數(僅適合單例/單線程)

int *putin() {
    static int arr[5]; // ✅ 靜態存儲區,程序全生命週期有效
    printf("請輸入 5 個數字: ");
    for (int i = 0; i < 5; ++i) scanf("%d", &arr[i]);
    return arr;
}

方案對比

方案優點缺點適用場景
主調分配/傳指針無動態分配開銷、所有權清晰、線程安全需知道大小、調用者必須提供緩衝區最通用、嵌入式/高性能首選
malloc/heap大小可動態決定、跨函數生命週期靈活需手動 free、碎片化、分配失敗處理大小運行時才知、需長期持有
static 區域無需管理記憶體、代碼最少非線程安全、不可重入、全域唯一實例單線程、單例模式、快速原型

進階:封裝為安全的「輸入+返回結構體」模式

typedef struct { int data[5]; int count; } IntArray;

IntArray readInts(int n) {
    IntArray arr = { .count = n };
    printf("請輸入 %d 個數字: ", n);
    for (int i = 0; i < n; ++i) scanf("%d", &arr.data[i]);
    return arr; // ✅ 結構體按值返回,C99 保證安全複製
}

int main() {
    IntArray a = readInts(5);
    for (int i = 0; i < a.count; ++i) printf("%d ", a.data[i]);
    return 0;
}

核心鐵律:**永遠不要返回指向區域變數(棧上陣列/結構體)的指針**。掌握「誰分配誰釋放」或「調用者提供緩衝區」兩大所有權模式,即可徹底避開野指針坑。

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