初學 C 語言時,遞歸與指針常被視為兩座大山。教科書往往照本宣科,缺乏從內存模型視角的底層剖析。本系列將從計算機體系結構出發,層層遞進講透指針本質。強烈推薦 Kenneth A. Reek 經典著作《Pointers On C》作為進階參考。
理解指針,必須先建立正確的內存心智模型。請牢記三個核心認知:
地址 (十六進制) 0x100 0x104 0x108 0x112 0x116
┌───────┬───────┬───────┬───────┬───────┐
內存內容 │ 542 │ 3.14 │ 'A' │ 12323 │ -12 │
└───────┴───────┴───────┴───────┴───────┘
▲ ▲ ▲ ▲ ▲
total_ Pi m_ch money number
count (char) (int)
關鍵洞察:編譯器將難記的數字地址映射為易讀的變量名(符號)。機器碼執行時,仍通過真實地址訪問內存——這就是編譯器的符號解析與重定位工作。
看四個聲明:
int total_count = 542;
float Pi = 3.14;
int *p1 = &total_count; // 整型指針
char *p2 = &m_ch; // 字符指針
前兩行是普通變量定義。後兩行中,類型後出現 *,標識這是指針變量(Pointer Variable)。
指針也是變量,但它只能存儲一種值——內存地址。
為什麼要區分 int*、float*、char* 等不同類型指針?畢竟地址本質上都是整數編碼。
答案在於:類型安全與指針算術運算。
編譯器強制類型匹配,在編譯期攔截絕大多數指針誤用,這是 C 語言「信任程序員但提供護欄」哲學的體現。
這兩個符號在不同語境下含義截然不同,是初學者最大的困惑源。請建立語境敏感的理解:
| 語境 | 符號 | 名稱 | 含義 | 示例 |
|---|---|---|---|---|
| 聲明中 | * | 聲明標識 | 標識該變量為指針類型 | int *p; |
| 表達式中 (前綴) | & | 取址運算符 | 獲取變量的內存地址 | &total_count → 0x100 |
| 表達式中 (前綴) | * | 間接訪問/解引用 | 訪問地址所指內存單元的值 | *p1 → 542 |
int total_count = 542; // 地址 0x100,值 542
int *p1 = &total_count; // p1 變量地址 0x200,值 0x100 (total_count 的地址)
int count = *p1; // 1. 讀取 p1 的值 → 0x100
// 2. 到地址 0x100 讀取 4 字節 → 542
// 3. 賦值給 count
// 等價於:int count = total_count;
int *p1 = &total_count;
// 錯誤認知:p1 == 542 (把指針當成了它指向的值)
// 正確認知:p1 == 0x100 (地址),*p1 == 542 (值)
// 打印指針地址值(十六進制)
printf("p1 address: %p\n", (void*)p1); // 輸出 0x100
printf("p1 value: %d\n", *p1); // 輸出 542
既然指針是變量,它也佔用內存,自然也有地址,於是產生指針的指針(Pointer to Pointer)。
int total_count = 542; // 0x100
int *p1 = &total_count; // p1 @ 0x200, 值 0x100
int **p2 = &p1; // p2 @ 0x300, 值 0x200 (p1 的地址)
// 解引用鏈:
*p2 → 0x100 (p1 的值,即 total_count 的地址)
**p2 → 542 (total_count 的值)
int **p2; // 讀作:p2 是 一個指針,指向 一個指針,指向 int
int ***p3; // 三級指針,實際編碼極少見超過二級
int count2 = *(*(&p1)); // 10秒內算出值?
// 逐層剝洋蔥(從最內層括號開始):
// 1. p1 → 值 0x100 (total_count 地址)
// 2. &p1 → 值 0x200 (p1 自身的地址,即 int** 類型)
// 3. *(&p1) → 解引用 0x200 → 獲得 0x100 (p1 的值)
// 4. *(*(&p1)) → 解引用 0x100 → 獲得 542 (total_count 的值)
// 結果:count2 = 542
「指針」≈「地址」 —— 在閱讀代碼時,將兩詞互換理解,雖不嚴謹但極其實用。
& 取地址,* 解引用(聲明時除外)。下一篇將深入 一維數組與指針的深度綁定關係,揭秘為什麼「數組名是指針常量」以及下標訪問的指針本質。