指標
一個裝著「地址」的變數。 · ⏱ 約 40 分鐘
你會學到三件事
& 拿地址、* 去那個地址取東西
- 為什麼
scanf 非要那個 & 不可
- 指標不是難,是多了一層
開始之前
到目前為止,一個變數裝的是值。
這一課的變數裝的是**「另一個變數在哪裡」**。
一、兩個符號
int n = 42;
int *p = &n;
|
唸作 |
意思 |
&n |
address of n |
n 在哪裡(一個地址) |
int *p |
p 是一個指向 int 的指標 |
p 裝的是地址 |
*p |
去 p 指的地方 |
那個地址上的東西(也就是 n) |
⚠️ * 出現在兩個地方,意思不一樣:
int *p = &n; // 宣告時的 * =「p 是指標」
*p = 100; // 使用時的 * =「去那裡」
這是初學者最容易糊掉的一點。記法:
宣告時的星號在講型別,使用時的星號在做動作。
二、一張圖
n p
┌──────┐ ┌──────┐
│ 42 │ ◄────────── │ 0x7f │
└──────┘ └──────┘
位址 0x7f 裝著 n 的位址
printf("%d\n", *p); // 42 ——去 0x7f 看,是 42
*p = 100; // 去 0x7f,把它改成 100
printf("%d\n", n); // 100 ← 🔴 n 也變了
n 和 *p 是同一個東西的兩個名字。
三、🔴 所以 scanf 需要 &
第 1 課那個沒解釋的 &,現在可以解釋了。
scanf("%d", &n);
scanf 要改變 n。而 C 傳參數的時候是傳值——
傳 n 進去的話,函式拿到的是一份複本,改它不會影響外面。
所以要傳「n 在哪裡」,函式才改得到本體。
& 不是 scanf 的怪癖,是 C 唯一能「改到外面」的方式。
⚠️ 這也是為什麼 scanf("%s", s) 不用 &——
s 是陣列,而陣列名本身就已經是位址了。
四、空指標
int *p = NULL; // 指向「什麼都沒有」
⚠️ 對 NULL 用 * 會讓程式當場當掉(segmentation fault)。
而那其實是好消息——一個指到亂七八糟地方的指標會安靜地毀掉別的東西,
而 NULL 至少會立刻停下來。
用之前先問:
if (p != NULL) {
printf("%d\n", *p);
}
完成的樣子
int main() {
int n = 42;
int *p = &n;
printf("%d\n", *p);
*p = 100;
printf("%d\n", n);
return 0;
}
換你了
寫一個函式 void swap(int *a, int *b),把兩個變數的值對調。
⚠️ 呼叫的時候要寫 swap(&x, &y)——不加 & 的話它交換的是複本,
而外面兩個變數紋風不動,程式不會報錯。
(這是 C 的經典練習,而它就是「為什麼需要指標」的答案。)
這一課你做了什麼
- 你用
& 拿到了一個變數的地址
- 你用
* 透過地址改到了原本的變數
- 你知道
scanf 的 & 是什麼、為什麼不能省
如果卡住了
| 你看到 |
多半是因為 |
| 程式當掉(segfault) |
對 NULL 或沒初始化的指標用了 * |
swap 沒有效果 |
呼叫時忘了 &,或函式參數沒寫 * |
scanf 之後值沒變 |
忘了 & |
| 印出一個很長的數字 |
印的是位址不是值。要 *p |
宣告時的 * 到底屬於誰 |
int *p 和 int* p 完全一樣。⚠️ 而 int* a, b; 裡 b 不是指標 |
在編輯器打開這一課 →