所謂的指標(Pointer),就是記憶體的地址。換句話說,把記憶體比喻成大樓,大樓裡每層都有其位址,而指標變數主要就是儲存某個東西在「第xxxxx樓」。
指標並沒有甚麼神奇的,它和int,float,char等一樣,可視為C語言的一種資料型別。所謂int變數
所謂pointer變數
指標的用法
&:取得變數的位址。
*:間接參考,指使用指標取得某個記憶體的內容。
範例:
int a=1, b=2, c;
int *p; //宣告p是指向int的指標,英文說成p is a pointer to int
p = &a; // &a是取a的地址(沒有&的話就變成取a的內容),=讓&a複製到p,也就是說現在p的內容為變數a的地址
b = *p; //b的值被設定為指標p所指到記憶體的值(沒有*的話就變成取p的內容,而不是透過p去取a的內容了)
*p = 0; //透過p將a的值被設定為0
p = &c; //指標p現在指向c
計算應用
*p = *p + 10; // 透過p把某記憶體(現在是c)的值增加10
y = *p + 1; //透過p取某記憶體的值(現在是c)加1之後,把結果存入y中
++*p; // 指標p所指到的記憶體上1,這是因為++和*優先權同樣為2,右結合,因此*先做,再做++
要特別提醒大家的是,int *x,這類的宣告只分配了存放pointer的空間,至於透過該pointer找到的記憶體內容是否合法,C語言就不管了。對初學者來說,常犯宣告了pointer,但對該pointer只到的空間卻沒有分配的錯誤。
#include <stdio.h> int main() { int x; int *p; p = 0; // 這讓p指到不合法的地方去了 *p = 0; // 透過p去設定不合法的記憶體, 會產生嚴重錯誤 x = *p; // 這也是錯的 }
在上面的例子中,*p=0的指令就足夠讓你的C程式一團混亂了。在Unix執行這樣的指令,會產生Segmentation Fault (core dumped),整個程式會被作業系統中斷掉,並在目前的目錄下產生名稱為core的檔案,以讓你利用除錯程式找尋錯誤。在Windows系統中,這類的錯誤會跳出對話方塊,告訴你該程式存取了某某不合法的記憶體,因此將被終止。至於DOS這類沒有保護記憶體的作業系統,則可能當作甚麼事也沒發生,但你跑起該程式就是有奇怪的現象發生。我曾聽過同學做某DOS專案的時候,就發生過螢幕畫面上有個點特別亮,找了很久才發現是pointer設錯了,以致透過該pointer把繪圖卡上面的記憶體改掉了!
指標裡的+運算符號,如pointer p + integer n,表示指標p往下指n個元素。-運算符號,如pointer p - pointer q,表示q和p之間距離了幾個元素,pointer p - integer n,表示指標往上指n個元素。
#include <stdio.h> int main() { int x[100]; int *p, *q; p = x; q = x + 20; *(p+10) = 100; // 相當於x[10] = 100 printf("offset between p and q is %d\", q - p); // 這會印出20 }
使用+-符號來移動指標要特別小心,不要讓它們指到不確定的地方去了,如:
#include <stdio.h> main () { float z = 5.5; int x = 5; int y = 5; int *p; p = &x; p = p + 1; // 想把p指到哪裡去啊? printf("%d %d %f\n", x, y, z); *p = 20; printf("%d %d %f\n", x, y, z); }
上述的例子裡,程式會順利跑完,但是y,z變成了甚麼?我沒辦法告訴你,因為在不同的環境下跑出來的結果很可能不同!要是不信就在Unix和Windows上跑跑看吧。
指標除了指到基本的資料型態外,也可以指到另一個指標
#include <stdio.h> main () { int y = 2; int x = 5; int *p; // p is a pointer to int int **q; // q is a pointer to pointer to integer p = &x; // 把p 指到 x q = &p; // 把 q 指到 p *q = &y; // 透過q把p的內容改為指到y printf("%d\n", *p); // 會印出2 printf("%d\n", **q); // 會印出2 }
C語言的參數傳遞使用Call by Value,因此被呼叫的函數無法直接存取呼叫函數內的變數。若要達到類似Call by Reference的效果,就要透過傳遞指標來達成。
void fun(int x, int y) { x = 5 y = 5; } void main() { int x = 0; int y = 0; fun(x, y); printf("%d %d\n", x, y); }如果要讓fun可以存取main裡面的x,y則程式應如下:
void fun(int * x, int * y) { *x = 5; *y = 5; } void main() { int x = 0; int y = 0; fun(&x, &y); printf("%d %d\n", x, y); }fun裡的xy和main裡面的xy是完全不同的東西,一指標,一是整數,但fun裡的pointer x,y其內容是main裡x,y的地址,因此透過pointer x,y就可以去存取到int x,y了。