二級指標(T**)是「指向指標的指標」,除能修改所指物件值外,更能修改一級指標的指向。本文從記憶體模型出發,對比一級/二級指標作為函數參數的語意差異,提供完整圖解與三種實戰範例,徹底釐清指標層級操作邏輯。
int m = 4, n = 5;
int *p = &m; // p 存放 m 位址
int **q = &p; // q 存放 p 位址
記憶體佈局:
q (位址 0x7000) ──► p (位址 0x6000) ──► m (位址 0x5000, 值 4)
│
└──► (可修改為 &n)
*q 等價於 p(一級指標值)。**q 等價於 *p 等價於 m(最終物件值)。| 參數型別 | 能修改物件值 | 能修改呼叫端指標指向 | 典型用途 |
|---|---|---|---|
int* (一級) | ✅ | ❌ (傳值,副本) | 輸出參數、陣列處理 |
int** (二級) | ✅ | ✅ (傳址,原指標) | 輸出指標、雙向鏈表頭插、工廠函數 |
#include <iostream>
using namespace std;
int num = 8;
void changePtr(int** pp) { // 接收一級指標的位址
*pp = # // 解引用一次,修改原 p 的指向
}
int main() {
int a = 5;
int* p = &a;
changePtr(&p); // 傳遞 p 的位址
cout << *p << ' ' << a; // 輸出:8 5 (p 已指向 num,a 不變)
}
void changeVal(int* p) {
int i = 0;
int* tmp = &i;
*p = 8; // 修改物件值
p = tmp; // 只修改區域副本,呼叫端 p 不受影響
}
int main() {
int a = 5;
int* p = &a;
changeVal(p);
cout << *p << ' ' << a; // 輸出:8 8 (a 被改為 8,p 仍指向 a)
}
void modify(int* p, int** q, int* z) {
int a = 10;
z = &a; // 只修改區域 z 指向,不影響呼叫端
*p = 10; // 修改 p 指向的物件值
**q = a; // 修改 q 指向的指標所指物件值
// *q = &a; // 若取消註解,可修改呼叫端一級指標指向
cout << "函數內: *p=" << *p << " **q=" << **q << endl;
}
int main() {
int i=4, j=5, k=6;
int *p=&i, *q=&j, *z=q; // z、q 同指向 j
*z = 8; // j 變 8
z = &k; // z 改指向 k,q 仍指向 j
cout << "呼叫前: *p=" << *p << " *q=" << *q << endl;
modify(p, &q, z); // 傳 q 位址
cout << "呼叫後: *p=" << *p << " *q=" << *q << endl;
}
輸出解析:
*p (i) 從 4 → 10*q (j) 從 8 → 10 (經 **q = a)z 區域修改不影響外部一級指標傳值:改值不改向。
二級指標傳址:改值又改向。
int createNode(Node** out) 由函數分配並回傳新節點位址。void push(Node** head, int val) 修改 *head 指向新節點。int** arr = new int*[rows],每列再 new int[cols]。HRESULT CreateInstance(IUnknown** ppv) 標準輸出介面指標模式。二級指標的本質是「指標的地址」。掌握 *pp 解引用一次得到一級指標、**pp 解引用兩次得到物件的雙重解引用機制,即可靈活實現「函數修改呼叫端指標指向」等進階模式。圖解記憶體、對比參數傳遞語意、多寫實戰範例,是攻克指標層級操作的最佳路徑。