指針的四大核心屬性 (必須能瞬間回答)
| 屬性 | 獲取方法 | 示例 int *p; | 意義 |
| 指針類型 |
聲明語句去掉變量名 |
int * |
決定指針本身佔用內存大小、解引用類型、指針算術步長 |
| 指向類型 |
聲明去掉變量名及最左 * |
int |
決定 *p 的類型、sizeof(*p)、訪問內存的方式 |
| 指向內存區 (值) |
賦值時決定 p = &x; |
&x |
指針指向的起始地址,佔用 sizeof(指向類型) 字節 |
| 指針自身內存 |
sizeof(p) |
4 字節 (32位) / 8 字節 (64位) |
存儲地址值的變量自身 |
複雜聲明拆解練習
int *p; // 類型:int* 指向:int
char *p; // 類型:char* 指向:char
int **p; // 類型:int** 指向:int*
int (*p)[3]; // 類型:int(*)[3] 指向:int[3] (指向3元素數組的指針)
int *(*p)[4]; // 類型:int*(*)[4] 指向:int*[4] (指向4元素指針數組的指針)
指針算術運算:步長由「指向類型」決定
char a[20]; int *p = (int*)a; // 強制轉換演示
p++; // p += sizeof(int) = 4 (32位)
/*
原指向 a[0..3] (4字節)
現指向 a[4..7] (下4字節)
*/
p + n → 地址增加 n * sizeof(指向類型) 字節。
- 指針減指針 → 返回元素個數差 (類型必須相同)。
- 越界指針:語法合法,解引用即 UB。
p+5 指向 a[20] 合法 (尾後指針),p+6 即 UB。
遍歷數組標準範式
int arr[20]; int *p = arr, *end = arr + 20;
for (; p != end; ++p) (*p)++; // 修改每個元素
運算符 & (取址) 與 * (解引用)
| 運算符 | 操作數 | 結果類型 | 結果值 | 結果左值性 |
& | 左值 (變量/數組元素/解引用) | 類型 + * | 操作數地址 | 右值 |
* | 指針類型 | 指針指向類型 | 指針指向的對象 | 左值 (可賦值) |
多級指針演示
int a = 12, b;
int *p = &a; // p: int*, 指向 int, 值 &a
int **ptr = &p; // ptr: int**, 指向 int*, 值 &p
*p = 24; // a = 24
**ptr = 34; // *ptr 是 p, **ptr 是 a → a = 34
指針表達式:結果為指針的表達式
int a, b, arr[10];
int *pa = &a; // &a 是指針表達式
int **ptr = &pa; // &pa 是指針表達式
pa = arr; // arr 退化為指針表達式
pa++; // pa+1 是指針表達式
char *str = *parr; // *parr (parr: char**) 是指針表達式
str = *(parr + 1); // *(parr+1) 同理
- 指針表達式同樣具備四大屬性。
- 左值語境 (賦值目標) 表示存儲地址;右值語境 表示讀取地址值。
進階專題:函數指針與回調
// 聲明:返回 int,參數 (int, int) 的函數指針類型
typedef int (*BinOp)(int, int);
int add(int a, int b) { return a + b; }
int mul(int a, int b) { return a * b; }
int compute(int x, int y, BinOp op) { return op(x, y); }
int main() {
printf("%d\n", compute(3, 4, add)); // 7
printf("%d\n", compute(3, 4, mul)); // 12
}
常見陷阱與最佳實踐清單
- 未初始化指針:定義即初始化
int *p = NULL; 或 int *p = &x;。
- 野指針:
free(p) 後立即 p = NULL;;函數不返回局部變量地址。
- const 正確性:
const int *p (指向常量) vs int *const p (常量指針) vs const int *const p。
- 指針 vs 數組:
sizeof(arr) = 整個數組大小;sizeof(p) = 指針大小。數組不退化時 (如 &arr) 類型為 int (*)[N]。
- 結構體指針訪問:
p->member 等價 (*p).member,前者更易讀。
- void 指針:通用字節指針,不可算術運算 (GCC 擴展允許按 1 字節),轉換為具體類型後再運算。