Semorphe

指標

一個裝著「地址」的變數。 · ⏱ 約 40 分鐘

你會學到三件事

  1. & 拿地址、* 去那個地址取東西
  2. 為什麼 scanf 非要那個 & 不可
  3. 指標不是難,是多了一層

開始之前

到目前為止,一個變數裝的是

這一課的變數裝的是**「另一個變數在哪裡」**。

一、兩個符號

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 的經典練習,而它就是「為什麼需要指標」的答案。)

這一課你做了什麼

如果卡住了

你看到 多半是因為
程式當掉(segfault) 對 NULL 或沒初始化的指標用了 *
swap 沒有效果 呼叫時忘了 &,或函式參數沒寫 *
scanf 之後值沒變 忘了 &
印出一個很長的數字 印的是位址不是值。要 *p
宣告時的 * 到底屬於誰 int *pint* p 完全一樣。⚠️ 而 int* a, b;b 不是指標
在編輯器打開這一課 →