Saturday, July 20, 2013

Variable, Pointer and Reference

    pointer 和 reference 都可以間接存取變數,pointer 和 C 的 pointer 是一樣的,reference 則是 C++ 才有。兩者的功能相似,但是語法和用法差還滿多的。
1. variable - 變數
恩..變數...沒什麼好說的 XD

2. pointer - 指標,指向某個變數的記憶體位址
    在指標變數裡存的是一個位址 (address),它可以指向某個變數,可以透過 * 運算元間接存取該變數;而要取得某個變數的記憶體位址,則是使用 & 運算元。
(1) 初始化
    指標變數的初始化是相當重要的。宣告後未經初始化的指標變數會發生悲劇 XD。其實應該這麼說,使用沒有初始化的指標變數不僅毫無意義,而且還會發生不可預期的錯誤。
    把指標變數想像成遙控器的話,它所指向的變數就是這支遙控器可以控制的對象。顯而易見的,我們必須清楚知道我們控制的是什麼東西,這樣的操控才有意義,好比說,把遙控器設成控制自己家的小狗 (如果可以的話),然後叫牠去把門口的報紙拿進來、順便泡杯咖啡之類的 (我根本在癡心妄想 XDDD)。
    好啦言歸正傳,如果少了「設成控制自己家的小狗」(也就是初始化) 這個動作,就直接拿著遙控器亂按,如果跑進來的是一隻恐龍還是蟑螂誰要負責 XDDDD。聽我在亂講,不會有奇怪的東西跑出來,只是程式可能會 crash 而已。
int var;
int *ptr;
*ptr = 0;  // dangerous!! (但 compile 會過)
ptr = &var;  // 把 ptr 指到 var 這個變數
*ptr = 3;  // 這個指令會把 var 設成 3
(2) 型別
    變數有形別之分,但是指標長得都一樣,這邊所謂的指標型別,說的是這個指標指向的變數的型別。
    比如說 int 是 4 bytes,那麼指向 int 變數的指標意義就是:從指到的位址開始往後算 4 格是這個指標變數所配置到的記憶體範圍 (我應該畫圖的,可是好懶 >"<);char 是 1 byte,那麼指向 char 變數的指標配到的記憶體範圍就只有指到的那格;double 是 8 bytes,所以會配到 8 格。
    雖然指到的位置可能都一樣,但是意義不一樣,所以如果型別混用的話,也是會 compile error 的。
int var;
int *ptr1 = &var;  // correct,ptr1 指到 var
double *ptr2 = &var;  // compile error,double* <== int*
    雖說如此,想要有個指標變數可以指到任意型別,也是可以辦到,用 void* 就行了。只不過如果用 void* 的話,在使用時要十分注意型別是否正確,否則可能出現不可預期的錯誤。
void *ptr;  // 一個 void 型別的指標
ptr = &var;  // correct
*ptr = 0;  // correct,但要小心這裡一定只能 assign int 的值
(3) 指標的加減
    延續上面說的型別,對一個指標變數做加法或減法,不同型別的指標變數會有不同的效果,因為指標的移動是以型別的 size 當單位的。
int iArr[10];
int *iptr = iArr;  // 把 iArr 的開始位址 assign 給 iptr
iptr++;  // iptr == &iArr[1],實際上移動了 4 byte

char cArr[10];
char *cptr = cArr;  // 把 cArr 的開始位址 assign 給 cptr
cptr++;  // cptr == &cArr[1],實際上移動了 1 byte
    void 不屬於任何一個型別,所以對 void 型別的 pointer 做加法或減法會 compile error。
void *ptr = iArr;
ptr++;  // compile error!!
    還有關於運算元的 priority 的部分,用 * 取值的 priority 低於 ++ 的 priority,因此下面兩個的意義是不同的
*ptr++;  // 取值之後把 ptr 往後移一格,相當於*(ptr++)
(*ptr)++;  // 把 ptr 指到的內容加 1
另外還有多重指標,像是 **ptr 什麼的,就不在這邊說了。

3. reference - 變數的別名 (alias)
    reference 和 pointer 一樣,可以用來間接存取變數。如果把 pointer 想成遙控器的話,在這邊可以把 reference 想成...替身吧。不同於 pointer 可以指來指去 (assign 不同變數的位址給同一個指標變數),一個 reference 在程式裡面是不能被 assign 成其他變數的替身。也就是說呢,一旦決定當誰的替身了,就會永遠變成那個變數 (這是不可逆的化學變化啊啊)。
    正因為它不能被 assign,所以宣告的時候就要同時初始囉,不然 compile 不會讓你通過 (You can not pass!!!)。
int var, var2 = 10;
int &ref1 = var;  // correct,把 ref1 當作 var 的替身
int &ref2;  // compile error,沒有初始化
從此之後見 ref1 如見 var !!!有關 reference 的使用
ref1 = var2;
上面那個指令會讓 var 的值變成 10 (也就是 var2 的值),相當於
var = var2;
這個指令,而不會再把 ref1 變成 var2 的替身噢。
而下面這個的話
ref1 = &var2;
明顯就是 compile error 噢!哪有人把 int 指標直接 assign 給 int 變數的呢!

4. pointer reference - pointer 的替身
    其實這應該是要來把大家搞混的 @@。根據字面上的意義呢,它是一個 reference,而這個reference 是一個 pointer 的替身。
int *ptr;
int *&refPtr = ptr;  // refPtr 是 ptr 的替身
    這要幹嘛呢?我猜大概就是為了彌補 reference 不能動來動去這件事吧。雖然 refPtr 只能代替 ptr,但是 ptr 本身可以動來動去,所以可以指到不同的變數。不知道實用性如何,不過我相信必然是很容易搞混的。
    還有就是,我剛剛試過了,沒有 reference pointer (也就是指向 reference 的 pointer) 那種東西 XD

    就一個 ACMer 的角度來說,pointer 這東西是少碰為妙。因為如果沒有很清楚的話很容易出錯或浪費時間,在比賽的時候浪費時間是罪過啊!所以 pointer reference 就更不用說了,看看就好,被搞混之後就可以忘記了 XDD。
    噢沒有啦,還是要知道一下 (才不會顯得無知!?),只是強烈建議不要拿來用啦,根本找自己麻煩 ˊ _>ˋ。

No comments:

Post a Comment