在《徹底搞定C語言指針》系列的第二篇中,我們已經深入剖析了指針的本質。今天我們將邁進更高階的領域——學習「指向另一指針地址的指針」,也就是常說的雙重指針或指針的指針。在正式開始前,讓我們快速回顧一下指針的基礎模型。
當程序聲明變量時:
short int i;
char a;
short int *pi;
編譯器會在內存中為每個變量分配空間。假設內存地址從 6 到 15,佈局如下:
內存地址 → 6 7 8 9 10 11 12 13 14 15
--------------------------------------------------
... | | | | | | | | | |
--------------------------------------------------
|short int i|char a| |short int *pi|
i 位於地址 6,佔 2 字節(短整型)a 位於地址 8,佔 1 字節pi 位於地址 10,佔 2 字節(此處假設 16 位系統,32 位系統為 4 字節)執行賦值:
i = 50;
pi = &i;
內存映像變為:
內存地址 → 6 7 8 9 10 11 12 13 14 15
--------------------------------------------------
... | 50 | | | 6 | | | | | |
--------------------------------------------------
|short int i|char a| |short int *pi|
此時 pi 的值為 6,即 i 的起始地址。對 *pi 的讀寫實際上就是對 i 的讀寫,例如 *pi = 5; 等價於 i = 5;。
指針變量本身也佔據內存地址(如 pi 在地址 10)。我們可以聲明另一個指針指向這個地址:
short int **ppi; // 注意兩個 * 號
ppi = π
ppi 為「指向 short int * 類型指針的指針」pi 的地址(10)賦給 ppi更新後的內存佈局:
內存地址 → 6 7 8 9 10 11 12 13 14 15
--------------------------------------------------
... | 50 | | | 6 | 10 | | | | |
--------------------------------------------------
|short int i|char a| |short int *pi|short int **ppi|
| 表達式 | 值 | 含義 |
|---|---|---|
ppi | 10 | 指針 pi 的地址 |
*ppi | 6 | pi 的值(即 i 的地址) |
**ppi | 50 | i 的值(即 *pi 的值) |
這層層解引用的機制,正是雙重指針的核心威力所在。
設計函數 find1(char array[], char search, char *pi):在以 \0 結尾的字符串 array 中查找字符 search,找到則通過第三個參數返回該字符地址,否則返回 0。
void find1(char array[], char search, char *pa) {
int i;
for (i = 0; *(array + i) != 0; i++) {
if (*(array + i) == search) {
pa = array + i;
break;
} else if (*(array + i) == 0) {
pa = 0;
break;
}
}
}
void main() {
char str[] = "afsdfsdfdf\0";
char a = 'd';
char *p = 0;
find1(str, a, p);
if (0 == p) {
printf("沒找到!\n");
} else {
printf("找到了,p=%d", p);
}
}
運行結果輸出「沒找到!」——明明字符串第 4 個字符是 'd',為什麼失敗?
函數調用時發生隱含賦值:
array = str;
search = a;
pa = p; // 值傳遞:pa 獲得 p 的地址值(此處為 0)
函數內部修改 pa 只是改變了形參的局部副本,無法影響實參 p 的值。這正是「值傳遞」的限制——即使傳遞的是地址值,也是按值傳遞地址。
改用 char **ppa 作為參數,調用時傳遞 &p:
void find2(char array[], char search, char **ppa) {
int i;
for (i = 0; *(array + i) != 0; i++) {
if (*(array + i) == search) {
*ppa = array + i; // 解引用一次,修改 p 的值
break;
} else if (*(array + i) == 0) {
*ppa = 0;
break;
}
}
}
// 主函數調用
find2(str, a, &p);
array = str;
search = a;
ppa = &p; // ppa 指向 p 的地址
函數內對 *ppa 的修改直接作用於 p,從而實現「在函數內修改指針變量本身」的目的。這是雙重指針最經典的應用場景之一。
類型 **變量名* 可訪問該指針的值,** 可訪問最終目標數據char **argv 等掌握雙重指針,標誌著你已跨越 C 語言指針學習的重要門檻,能夠處理更複雜的內存管理與數據結構問題。