未初始化的指針指向隨機地址,直接使用會破壞系統數據甚至導致死機。本文以 Turbo C 2.0 為背景,詳細演示 **malloc 動態分配正確模式**、**指針算術與類型大小的關係**、**多元素連續分配** 實戰,並揭示 *(p+1) ≠ 下一字節的核心誤區。
/* 危險:野指針 */
main() {
int *i;
char *str;
*i = 420; // 崩潰/亂寫
str = "Good"; // 僅修改指針指向常量區,未分配可寫空間
}
#include /* 或 */
main() {
int *i;
char *str;
/* 分配 1 個 int (2 字節) */
i = (int*)malloc(sizeof(int));
if (!i) exit(1); // 必檢查 NULL
*i = 420; // 正確寫入
/* 分配 20 字節字符緩衝區 */
str = (char*)malloc(20);
if (!str) exit(1);
strcpy(str, "Good, Answer!"); // 安全拷貝
/* 使用完畢必須釋放 */
free(i);
free(str);
}
#include
main() {
int *a;
a = (int*)malloc(3 * sizeof(int)); // 分配 3 個 int (6 字節)
if (!a) exit(1);
*a = 1234; // a[0]
*(a + 1) = 4567; // a[1]
*(a + 2) = 234; // a[2]
/* 等價下標寫法 */
a[0] = 1234;
a[1] = 4567;
a[2] = 234;
free(a);
}
*(p+1) 不是下一字節!指針算術以「所指類型大小」為單位縮放,而非位元組。
| 指針類型 | sizeof | p+1 位移位元組 | 等價下標 |
|---|---|---|---|
int * | 2 | 2 | p[1] |
char * | 1 | 1 | p[1] |
double * | 8 | 8 | p[1] |
struct Stu * | 結構大小 | 結構大小 | p[1] |
因此:int *a; *(a+1) = 4567 寫入的是**第 2 個 int (偏移 2 字節)**,而非第 2 字節。
malloc 失敗返回 NULL (0)。if (!ptr) { fprintf(stderr, "OOM\n"); exit(1); }。NULL → 段錯誤/崩潰。/* 分配 → 檢查 → 使用 → 釋放 → 置 NULL */
int *p = (int*)malloc(N * sizeof(int));
if (!p) handle_oom();
for (int i = 0; i < N; i++) p[i] = compute(i);
process(p, N);
free(p);
p = NULL; // 防止懸空指針二次 free / 使用
alloc.h (Borland 專用) 或 stdlib.h (ANSI)。malloc 返回對應模式指針。farcalloc/farmalloc 可跨 64KB 段分配 (大模式專用)。| 錯誤 | 後果 | 修正 |
|---|---|---|
malloc(sizeof(p)) | 分配指針大小而非目標類型 | malloc(sizeof(*p)) 或 malloc(sizeof(int)) |
忘記 free | 記憶體洩漏 | 嚴格配對 malloc/free |
free(p); free(p); | 二次釋放 → 堆損壞 | free(p); p = NULL; |
| 釋放非 malloc 記憶體 | 未定義行為 | 僅釋放 malloc/calloc/realloc 返回指針 |
| 指針算術按字節計算 | 訪問錯誤元素/越界 | 牢記:按 sizeof(*p) 縮放 |
malloc + 指針算術 = C 語言**動態記憶體管理**的核心原語。三大鐵律:**檢查 NULL、按類型縮放算術、用完即 free 置 NULL**。掌握此道,即可構建動態陣列、鏈表、樹、圖、哈希表等所有高級數據結構的存儲骨架。