概念不清:*p(指標指向的**值**)vs p(指標本身的**地址值**)。只要搞懂這對關係,指標就不再是難點。
int a, b;
scanf("%d,%d", &a, &b);
int *p1 = &a, *p2 = &b, *tmp;
if (a < b) {
tmp = p1; // 暫存 p1 指向的地址(&a)
p1 = p2; // p1 指向 b
p2 = tmp; // p2 指向 a
}
printf("%d,%d", *p1, *p2); // 輸出較大、較小值
初始: 交換後: p1 ───► a=3 p1 ───► b=5 p2 ───► b=5 p2 ───► a=3 *p1=3, *p2=5 *p1=5, *p2=3
關鍵:原變數 a,b **值未變**,僅透過指標間接輸出達到「排序」效果。理解 tmp 保存的是**地址**,非值。
void compositor(int *p1, int *p2) {
int temp;
if (*p1 < *p2) { // 比較指向的值
temp = *p1; // 暫存值
*p1 = *p2; // 修改 p1 指向的記憶體(實際修改 a)
*p2 = temp; // 修改 p2 指向的記憶體(實際修改 b)
}
}
int main() {
int a, b;
scanf("%d,%d", &a, &b);
compositor(&a, &b); // 傳遞地址
printf("%d,%d", a, b); // a,b 真實被修改
}
| 特性 | 範例一 | 範例二 |
|---|---|---|
| 修改實參值? | 否(僅交換指標指向) | 是(透過解引用寫入記憶體) |
| 暫存變數類型 | int*(地址) | int(值) |
| 適用場景 | 不想動原資料、需保留原順序 | 需真實交換/修改呼叫端變數 |
printf("%p %p %d\n", p, &a, *p); 同時印地址與值。| 錯誤代碼 | 問題 | 修正 |
|---|---|---|
int *p; *p = 10; | 野指標解引用 → 崩潰 | int a; int *p = &a; *p = 10; |
void f(int p) { *p = 1; } int a; f(a); | 傳值非傳址,形參非指標 | void f(int* p) { *p = 1; } f(&a); |
int *p = 0x1234; *p = 5; | 任意地址寫入 → 未定義行為 | 僅用於記憶體映射 I/O、嵌入式特定場景 |
int a[10]; int *p = a; p++; 以為 a 變了 | a 為常量指標不可修改,p 是副本 | 理解陣列名退化為右值指標 |
T* 或 T* const 表示「看著這個物件,不負責刪除」。std::unique_ptr (獨佔)、std::shared_ptr (共享)。new/delete:改用 make_unique/make_shared、標準容器。這兩個範例精準切中指標學習的核心:**地址與值的雙重身分、傳址呼叫修改實參、暫存變數類型選擇**。配合畫圖、輸出觀察、註釋三步驟,反覆練習至能心中自動運行記憶體模型。現代 C++ 將指標「降級」為非擁有觀察者,擁有權交給 RAII 類型,讓指標回歸本質——**單純的地址運算工具**。