所謂的指標(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了。