int a[5] = {1,2,3,4,5};
int *p = a; // p 指向 a[0]
p[2] == *(p+2) == a[2] == *(a+2) // 完全等價
// 關鍵:a 是常量指針,不可修改;p 是變量,可 p++ 遍歷
指針算術:`p + n` = 地址 + n × sizeof(*p)。`p++` 移動到下一個元素。
int a[3][4] = {
{1,2,3,4},
{5,6,7,8},
{9,10,11,12}
};
// 關鍵等價關係
a == &a[0] // 首行地址 (int (*)[4])
a[0] == *(a+0) // 第 0 行首元素地址 (int *)
a[1] == *(a+1) // 第 1 行首元素地址
&a[0][0] == *a // 第 0 行第 0 列元素地址
a[1][2] == *(*(a+1)+2) // 第 1 行第 2 列元素值
// 遍歷二維數組指釖法
int (*prow)[4] = a; // 指向「含 4 個 int 的數組」的指針
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 4; ++j)
printf("%d ", (*prow)[j]); // 或 prow[i][j]
++prow;
}
| 特性 | 二維數組 int a[3][4] | 指針數組 int *p[3] |
|---|---|---|
| 內存佈局 | 連續 3×4 個 int | 3 個離散指針,各指向獨立 1D 數組 |
| 行長度 | 固定 4 | 每行可異(鋸齒數組) |
| 分配方式 | 一次性棧/靜態/堆分配 | 指針數組 + 逐行 malloc |
| 訪問語法 | `a[i][j]` | `p[i][j]` 相同語法,語義不同 |
int a = 10;
int *p = &a; // 一級
int **pp = &p; // 二級
int ***ppp = &pp; // 三級
// 解引用層級對應
***ppp = 20; // 修改 a
**pp = 30; // 修改 a
*p = 40; // 修改 a
// 典型用途:函數修改指針本身(如鏈表頭插、動態分配輸出參數)
void alloc_int(int **out) {
*out = malloc(sizeof(int));
**out = 42;
}
int *p; alloc_int(&p); // p 指向新分配的 42
char *s = "Hello"; // s 指向唯讀區 "Hello\0"
char arr[] = "Hello"; // arr 複製到棧,可修改
s[0] = 'h'; // 崩潰!
arr[0] = 'h'; // OK
// 字符串處理函數本質:接收 char*,按 \0 遍歷
puts(s); // 等價 fputs(s, stdout)
printf("%s", s);
// 定義:返回 int,兩 int 參數的函數指針類型
int (*pf)(int, int);
// 別名
typedef int (*BinaryOp)(int, int);
int add(int a, int b) { return a+b; }
int sub(int a, int b) { return a-b; }
BinaryOp ops[] = {add, sub}; // 指針數組實現策略模式
printf("%d\n", ops[0](3,4)); // 7
memcpy/qsort 參數均用 `void*`。| 錯誤代碼 | 問題 | 修正 |
|---|---|---|
| `int *p; *p = 10;` | 野指針解引用 | `int *p = malloc(...);` 或 `int a; p=&a;` |
| `free(p); p[0]=1;` | 釋放後使用 | `free(p); p = NULL;` |
| `char *s = "abc"; s[0]='x';` | 修改字符串常量 | `char s[] = "abc";` |
| `scanf("%d", p);` | 傳指針值而非地址 | `scanf("%d", &x);` 或 `scanf("%d", p);` (p已是地址) |
| `int a[10]; a = p;` | 數組名不可修改 | `int *p = a;` 只能指針指向數組 |
指針是 C 語言靈魂:地址、類型、解引用、算術 四要素貫穿數組、字符串、結構體、函數、動態內存。精通指針等價於精通 C 內存模型。建議動手繪圖驗證每個 `*`、`&`、`[]`、`->` 的內存動作,構建心智模型:指針 = 地址 + 類型視角。