close

指標的藝術,就是就如同是內功心法一樣,一般程式師只懂的其意,但使用時,如同武學一樣,懂得門面上的套路,卻不一定懂的如何去運用。

何謂指標?就是用來記錄"位址"的變數,或者想像它的值是用來指向某個空間。例如:


int a;
int *b = &a;

b記錄的便是變數a所在空間的位址,也就是指向變數a所在的地方。因此*b=10的結果,便是將10填入b所指的地方,也就是變數a的實體空間裡,其結果跟a=10是一樣的。

void* p;

這樣的寫法表示,p指向的是一個空間,但這個空間裡記錄的是什麼結構並不知道。因此在不知指向的空間是什麼結構前,你無法直接填值進去,必須另外轉型才行。

char** p;

這樣的話,p指向的空間裡,是記錄char*這樣的結構也就是再記錄一個位址值。因此*p存取到的,都是位址值。取到位址值後,必須再指向一次,才能得到實際的空間。也就是**p才是指向記錄char這個結構的空間。

陣列變數其實也可以視為指標來看待:

int p1[10];
int *p2 = p1;

p1p2指向的空間位址都是一樣的,只不過p1可以知道是一個連續10int空間的陣列。p2的話,只知道是指向一個記錄int的空間,是不是陣列則是程式師才知道。在這個例子中,因為已經知道p2指向的是一個int陣列,所以可以用p2[5]這樣的方式來存取陣列內容。或者也可以寫成*(p2+5),它的意義則是p2指向的位址再往下5個結構,然後存取裡面的值。因為p2是指向的空間裡,記錄的是int結構,所以再往下5int便是第6int空間p2[5]是相同的。

int p1[10][20];
int **p2;

二維以上的陣列與指標就不太一樣,p1是指向一個一大塊的空間,這個空間被規劃成10x20intp2則是指向一個int*的空間。然後再從該空間裡記錄的位址再去存取實際的內容,這種方法為間接存取。假設p2是被做成跟p1一樣10x20維度的陣列,那麼p2在空間的運用上也與p1不同。因為p1指向的空間是一整塊,維度也是已知所以p1[i][j]馬上可以知道是在記錄空間裡的那一個地方。但p2就不同了,p2必須是先指向一個int*[10]的空間,每個空間分別指向int[20]的空間,因此實際的空間並非一大塊,而是拆成11其中一個是用來存放其他空間的位址用的。因此p2[i][j]實際上是先p2[i]int*[10]空間裡取到指標值, 然後再經由這個指標值再做[j]得到實際的空間, 然後進行存取。

但是一般人會認為指標好處是節省記憶體。指標的好處不是節省記憶體,大部份的情況反而比較浪費記憶體。但指標的目的,多半是彈性的處理,特別是不知道資料量大小的情況下,往往必須使用動態記憶體,此時便需要配合指標來處理。

如下兩種方式宣告,但是目的都只是放數值:
int *p;
*p=1;


int p;
p=1;
上面範例大部分還是使用int p


那怎樣是使用指標的時機呢?
如同上面範例p是用來指向一個int空間, 但沒給初值,也就是不知指向那裡,因此將1存入不知那個地方,一定會出問題。指標變數本身的空間 (也是有佔用空間唷),是用來記錄位址,所以填入p的值必須是某個空間的位址,在p沒指向一個切確的空間前,是不能去存取它指向的空間內容。即使有指向某個空間,也要看該空間還是否合法,像指向的動態記憶體被釋放掉了,或是指向的區域變數已離開它所在的scope等,這時原指向的空間已消失,再去存取,常會造成問題。

int p;
p=1;

這樣的變數p,本身空間是一個整數,因此存取的,都是整數值。

每個變數宣告後,都會佔用一定的空間,至於佔用多少空間、存取的內容是什麼,便視變數的形態而定。範例中int*p; p佔用sizeof(int*)空間存取的內容是int*結構的位址值。int p; p佔用sizeof(int)空間存取的內容是int結構的整數值。

雖然這些都是基本的觀念,但仍然要多用不同的寫法去體悟箇中意境,才能了解指標是如何使用。

 

-雲遊山水為知己、逍遙一生而忘齡-

arrow
arrow

    ETroy 發表在 痞客邦 留言(0) 人氣()