指標之所以讓人「暈」,是因為缺乏對「電腦如何組織資料」的直觀理解。本文從記憶體物理組織建立核心模型,涵蓋指標宣告、位址運算、多級指標、常見錯誤與建立直覺的練習路徑。
int a = 100;
float f = 2.11f;
// 記憶體中只有位元組序列:0x64 0x00 0x00 0x00 ...
// 型別告訴編譯器如何解讀:
// - int:取 4 位元組,有號整數解讀 → 100
// - float:取 4 位元組,IEEE 754 解讀 → 2.11
核心洞見:記憶體無型別,**型別是編譯期附加在存取操作上的解讀規則**。
int a = 100;
int* pa = &a; // pa 型別:int* (指向 int 的指標)
// &a:取址運算子,產生 a 的位址 (指標常數)
// *pa:解參考運算子,訪問 pa 指向的 int 物件
// 等價驗證
*a = 100; // 直接修改
*(&a) = 100; // 取址後解參考,等價
*&a = 100; // 運算子優先級相同,右結合,等價
int a = 100;
int* pa = &a; // 一級指標
int** ppa = &pa; // 二級指標 (指向指標的指標)
a; // 100
*pa; // 100
**ppa; // 100
pa; // &a (位址)
ppa; // &pa (位址)
*ppa; // pa (一級指標值)
char ch = 'a';
char* pch = &ch;
// 問題與答案
&ch; // ch 的位址 (char*)
pch; // ch 的位址 (char*)
&pch; // pch 自身的位址 (char**)
*pch; // 'a' (char)
*pch + 1; // 'b' (char + int → int,值 98)
*(pch + 1); // 未定義行為!pch+1 指向無效記憶體
++pch; // pch 指向下一個 char (位址+1),傳回新位址
*++pch; // 先遞增 pch,再解參考 (UB:指向無效記憶體)
*pch++; // 解參考舊 pch,再遞增 pch (傳回 'a',pch 後移)
++*pch; // 解參考 pch 得到 'a',遞增為 'b',ch 變 'b'
(*pch)++; // 同上,ch 變 'b'
++*++pch; // 先遞增 pch,再解參考遞增 (UB)
++*pch++; // 解參考舊 pch 遞增,再遞增 pch (ch 變 'b', pch 後移)
int* p; // 未初始化,含隨機位址
*p = 12; // 寫入隨機位址 → 段錯誤/資料破壞/靜默錯誤
// 正確模式
int* p = NULL; // 或 nullptr (C++11)
int* p = &variable; // 指向有效物件
int* p = malloc(...); // 指向動態記憶體
int arr[10];
int* p = arr; // 陣列名退化為首元素位址
p[3] = 5; // 等價 arr[3] = 5
*(p+3) = 5; // 等價
// 關鍵差異
sizeof(arr) // 40 (整個陣列大小)
sizeof(p) // 8 (指標大小,64位系統)
// 函數參數退化
void func(int a[10]) { sizeof(a); } // 實際為 int* a,sizeof=8
指標不是黑魔法,而是**記憶體位址的型別化封裝**。建立「記憶體格子 + 位址箭頭 + 型別眼鏡」的三維心智模型,配合大量手繪追蹤練習,指標就會從恐怖傳說變成精準可控的系統級工具。