C 指標入門與最常見的錯誤
變數住在記憶體裡,每一格都有自己的門牌號碼(地址)。指標,就是「存門牌號碼」的變數。 這篇用最少的符號講清楚指標在做什麼,最後整理初學者最常踩的幾個坑。
先分清楚兩個符號
&x:取出變數x的地址。int *p:宣告p是「存 int 地址」的變數。*p:照著p存的地址走過去,讀或寫那一格。
宣告時的
*表示「這是指標」;運算式裡的*p表示「去那個地址」。同一個符號,兩種意思。
& 與 * 實際跑一次
#include <stdio.h>
int main(void) {
int x = 10;
int *p = &x; // p 存的是 x 的地址
printf("x = %d\n", x);
printf("*p = %d\n", *p);
*p = 20; // 透過 p 改到 x
printf("x = %d\n", x);
printf("p == &x ? %d\n", p == &x);
return 0;
}輸出
x = 10
*p = 10
x = 20
p == &x ? 1*p = 20 沒有直接寫到 x,但因為 p 指向 x,改的就是 x 那一格。
為什麼 swap 一定要傳指標?
C 的參數是傳值:函式拿到的是一份副本,改副本不會影響呼叫端。想讓函式改到呼叫端的變數,就把地址傳進去,函式再用 *a、*b 改原本那一格。
#include <stdio.h>
void swap_value(int a, int b) {
int t = a; a = b; b = t; // 只換到副本
}
void swap_ptr(int *a, int *b) {
int t = *a; *a = *b; *b = t;
}
int main(void) {
int x = 3, y = 7;
swap_value(x, y);
printf("swap_value: x=%d y=%d\n", x, y);
swap_ptr(&x, &y);
printf("swap_ptr: x=%d y=%d\n", x, y);
return 0;
}輸出
swap_value: x=3 y=7
swap_ptr: x=7 y=3同樣叫 swap,輸出完全不同。追蹤程式(例如 APCS 程式識讀的 C 題本)時,要能一眼看出參數是傳值還是傳地址。
最常見的錯誤一:沒初始化就用指標
錯誤寫法
int *p; // p 指向哪裡?沒有人知道
*p = 10; // 未定義行為:可能當掉、可能改壞別的資料修正寫法
int x;
int *p = &x; // 先指向一個真的變數
*p = 10; // 等於 x = 10
int *q = NULL; // 還沒有目標就先設 NULL
if (q != NULL) *q = 10;編譯時加上 -Wall,gcc 會警告 'p' is used uninitialized。警告不是雜訊,要看。
最常見的錯誤二:陣列傳進函式後用 sizeof 算長度
陣列傳進函式時,函式拿到的其實是指向第一個元素的指標,所以在函式裡 sizeof(a) 算的是指標的大小,不是整個陣列。
錯誤寫法
int sum(int a[]) {
int n = sizeof(a) / sizeof(a[0]); // 錯:a 在這裡是指標
...
}正確的做法是把長度一起傳進去。不打算修改陣列時,加上 const:
#include <stdio.h>
int sum(const int *a, int n) {
int total = 0;
for (int i = 0; i < n; i++) {
total += a[i]; // a[i] 就是 *(a + i)
}
return total;
}
int main(void) {
int arr[] = {1, 2, 3, 4};
printf("sizeof(arr) = %zu\n", sizeof(arr));
printf("sum = %d\n", sum(arr, 4));
return 0;
}輸出
sizeof(arr) = 16
sum = 10sizeof(arr) = 16 是在 int 佔 4 bytes 的環境下的結果;在 main 裡 arr 還是陣列,所以算得到整個陣列的大小。
一次記起來
&x拿地址,*p照地址讀寫。- 參數是傳值;要改呼叫端的變數,就傳
&x。 - 指標宣告時就給值:
&變數或NULL。 - 陣列傳進函式會變成指標,長度要另外傳。
- 編譯加
-Wall,很多指標錯誤會被抓到。
想看更完整的說明,可以讀開源課本《C 程式設計》的〈指標〉一章;有問題歡迎到 Discord 發問。
資料來源
想有人逐行看你的程式?到 Discord 發問(免費),或看看我們的 線上程式家教與小班課。