指標是 C 語言的靈魂,也是初學者最大的門檻。本文從記憶體位址觀念出發,系統講解指標定義、解引用、取址、多級指標、void 指標、空指標、常見錯誤與除錯技巧,並提供直觀圖解與可執行範例代碼。
int a = 10; → 記憶體某位置存放 10int *p = &a; → 記憶體另一位置存放 a 的位址* 宣告時表「這是指標」;表達式中表「取此位址的值」(解引用)& 表「取得此變數的位址」#include <stdio.h>
int main() {
int a = 10; // 一般整數變數
int *p; // 宣告指向 int 的指標
p = &a; // 取得 a 的位址,存入 p
printf("a 的值: %d\n", a); // 10
printf("a 的位址: %p\n", (void*)&a); // 例如 0x7ffd...
printf("p 存放的位址: %p\n", (void*)p); // 同 &a
printf("p 指向的值: %d\n", *p); // 10 (解引用)
*p = 20; // 透過指標修改 a 的值
printf("修改後 a: %d\n", a); // 20
return 0;
}
| 錯誤代碼 | 問題 | 修正 |
|---|---|---|
int *g; g = 20; | 指標直接賦予整數常值 | int val = 20; g = &val; 或 int *g = malloc(sizeof(int)); |
int *p; *p = 10; | 未初始化指標解引用(野指標) | int a; int *p = &a; 或 malloc |
free(p); *p = 5; | 釋放後繼續使用(懸空指標) | free(p); p = NULL; |
int *p = &localVar; return p; | 返回區域變數位址(棧記憶體失效) | 返回動態分配記憶體、靜態變數、或呼叫端提供緩衝區 |
int a = 100; int *p1 = &a; // 一級指標:指向 int int **p2 = &p1; // 二級指標:指向 int* int ***p3 = &p2; // 三級指標:指向 int** // 記憶體佈局: // p3 (位址) → p2 (位址) → p1 (位址) → a (值 100) // 解引用層層遞進: **p2 = 200; // 等同 *p1 = 200; 等同 a = 200; ***p3 = 300; // 等同 a = 300;
void *ptr; 可存放任意型別位址void printInt(void *data) {
int *p = (int*)data; // 強制轉型
printf("%d\n", *p);
}
void printFloat(void *data) {
float *p = (float*)data;
printf("%.2f\n", *p);
}
NULL 定義為 ((void*)0),表示「不指向任何有效物件」if (ptr != NULL) { ... }int arr[5] = {1,2,3,4,5};
int *p = arr; // 陣列名退化為首元素位址
p[2] == *(p+2) == arr[2] == *(arr+2) // 完全等價
// 區別:sizeof
sizeof(arr) = 5 * sizeof(int) = 20 // 整個陣列大小
sizeof(p) = sizeof(int*) = 8 // 指標大小 (64位系統)
// 宣告:回傳 int,接受兩個 int 參數的函數指標
int (*funcPtr)(int, int);
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
funcPtr = add;
printf("%d\n", funcPtr(3, 5)); // 8
funcPtr = sub;
printf("%d\n", funcPtr(3, 5)); // -2
// 陣列:10 個函數指標
int (*funcArray[10])(int, int);
| 宣告 | 含義 |
|---|---|
int a; | 整數變數 |
int *a; | 指向整數的指標 |
int **a; | 指向指標的指標 |
int a[10]; | 10 個整數的陣列 |
int *a[10]; | 10 個指向整數指標的陣列 |
int (*a)[10]; | 指向「10 個整數陣列」的指標 |
int (**a)[10]; | 指向「指向 10 整數陣列指標」的指標 |
int *(*a)[10]; | 指向「10 個整數指標陣列」的指標 |
int (*a)(int); | 指向函數的指標 (int 參數, 回傳 int) |
int (*a[10])(int); | 10 個函數指標的陣列 |
-Wall -Wextra -Wpedantic -Wshadow -Wconversionclang --analyze、cppcheck、Coverity-fsanitize=address、Valgrindint *p = NULL; 宣告即初始化free,文檸化 API 契約指標雖難,但掌握「位址、值、解引用、型別」四要素,配合畫記憶體圖、單步除錯觀察暫存器與記憶體變化,就能建立堅實的直覺。建議從基礎操作練習開始,逐步挑戰鏈表節點操作、動態二維陣列分配、函數指標回調等實戰題目。