Last Updated on 2021-04-29 by Clay
指標(pointer)是一種指向變數記憶體位址的變數。一般而言,當我們今天跟系統宣告了一個變數並賦值時,程式會向記憶體申請空間來儲存:
- 變數位址(起始位址)
- 變數值
- 變數名稱
而使用指標,我們便能宣告一個變數指向某個特定變數的記憶體位址(搭配 &
符號取得地址)。
這樣做有什麼好處呢?指標其實是存在許多優點的,比方說:
- 替函式提供傳入參數的方法(比方說陣列(Array))
- 減少複製大塊記憶體,減少資源浪費
- 支援某些資料結構(鏈結串列(Linked-List))
當然指標也不是沒有缺點的,有時錯誤的使用方法甚至會引起系統崩潰。
如何使用指標
要宣告指標,需要在資料型態後加上 *
符號。
data-type *p;
而怎麼進行指標變數的操作呢?我們來看段簡短的範例程式碼:
#include <stdio.h> // Main int main() { int x=1, y=2, z[10]; int *p; // p is a pointer to int p = &x; // p now points to x y = *p; // y is now 1 *p = 0; // x is now 0 p = &z[0]; // p is now points to z[0] return 0; }
指標與函式參數
剛剛在闡述指標優點時有提到,指標替函式提供了傳入參數的方法。這是什麼意思呢?
我們來設計一個函式 swap(x, y)
,目的是將 x
與 y
兩個變數的值做交換。
錯誤示範
#include <stdio.h> void swap(int x, int y) { int temp = x; x = y; y = temp; } // Main int main() { int x=1, y=2; swap(x, y); printf("x: %d\ny: %d\n", x, y); return 0; }
Output:
x: 1
y: 2
如你所見,x
和 y
的值根本沒有交換。這是因為在 x
和 y
的值傳入 swap(x, y)
時另外複製了一份,而在 swap(x, y) 中交換的正是複製的變數,而無法真正影響外部傳入的變數。(當然,你可以回傳交換後的變數值,然後再次宣告 x
和 y
)
正確方法
#include <stdio.h> void swap(int *px, int *py) { int temp = *px; *px = *py; *py = temp; } // Main int main() { int x=1, y=2; swap(&x, &y); printf("x: %d\ny: %d\n", x, y); return 0; }
Output:
x: 2
y: 1
可以看到,使用指標操作的話,就像是操作原本的變數交換值一樣。
指標與陣列
在宣告陣列(Array)之後,我們變數記憶體位址即是首項元素的記憶體位址。來看段範例程式碼(%p
為印出記憶體位址)。
#include <stdio.h> // Main int main() { int a[3] = {1, 2, 3}; printf("a: %p\n", a); printf("&a[0]: %p\n", &a[0]); return 0; }
Ouptut:
a: 0x7ffee0c7c47c
&a[0]: 0x7ffee0c7c47c
記憶體位址是不是一模一樣呢?
除此之外,陣列的索引(index)則是首位元素記憶體位址的『位移量』。只要取得首項元素的記憶體位址賦值給指標變數,每次增加一個單位逐項增加,便會與陣列不同元素的記憶體位置相同。
#include <stdio.h> // Main int main() { int a[3] = {1, 2, 3}; int *p = a; for (int i=0; i<sizeof(a)/sizeof(a[0]); ++i) { printf("p + %d: %p\n", i, p+i); printf("&a[%d]: %p\n", i, &a[i]); printf("===================================\n"); } return 0; }