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