1. 程式人生 > >學習Linux C程式設計之陣列與指標

學習Linux C程式設計之陣列與指標

1.    指標型別分析

分析指標,可以從變數名處起,根據運算子優先順序結合,一步一步分析. 
int p;  //這是一個普通的整型變數 
int *p;  //首先從P處開始,先與*結合,所以說明P是一個指標,然後再與int結合,說明指標所指向的內容的型別為int 型.所以 P是一個返回整型資料的指標 
int p[3]; //首先從P處開始,先與[]結合,說明P 是一個數組,然後與int結合,說明數組裡的元素是整型的,所以 P是一個由整型資料組成的陣列 
int *p[3]; //首先從P處開始,先與[]結合,因為其優先順序比*高,所以P是一個數組,然後再與*結合,說明數組裡的元素是指標型別,然後再與 int結合,說明指標所指向的內容的型別是整型的,所以是一個由返回整型資料的指標所組成的陣列 
int (*p)[3];

 //首先從P處開始,先與*結合,說明P是一個指標然後再與[]結合(與"()"這步可以忽略,只是為了改變優先順序),說明指標所指向的內容是一個數組,然後再與int 結合,說明數組裡的元素是整型的.所以P是一個指向由整型資料組成的陣列的指標 
int **p; //首先從 P開始,先與*結合,說明P是一個指標,然後再與*結合,說明指標所指向的元素是指標,然後再與 int結合,說明該指標所指向的元素是整型資料. 所以P是一個返回指向整型資料的指標的指標 
int p(int);  //從P處起,先與()結合,說明P是一個函式,然後進入()裡分析,說明該函式有一個整型變數的引數然後再與外面的int 結合,說明函式的返回值是一個整型資料.所以P是一個有整型引數且返回型別為整型的函式 
int (*p)(int);
 //從P處開始,先與指標結合,說明P是一個指標,然後與()結合,說明指標指向的是一個函式,然後再與()裡的int 結合,說明函式有一個int 型的引數,再與最外層的int 結合,說明函式的返回型別是整型,所以P是一個指向有一個整型引數且返回型別為整型的函式的指標 
int *(*p(int))[3]; //從 P開始,先與()結合,說明P是一個函式,然後進入()裡面,與int結合,說明函式有一個整型變數引數,然後再與外面的*結合,說明函式返回的是一個指標,,然後到最外面一層,先與[]結合,說明返回的指標指向的是一個數組,然後再與*結合,說明數組裡的元素是指標,然後再與int 結合,說明指標指向的內容是整型資料.所以P是一個引數為一個整數且返回一個指向由整型指標變數組成的陣列的指標變數的函式 

2. 運算子&和* 
&是取地址運算子,*是間接運算子。 
&a的運算結果是一個指標,指標的型別是a的型別加個*,指標所指向的型別是a的型別,指標所指向的地址嘛,那就是a的地址。 
*p的運算結果就五花八門了,總之*p 的結果是 p 所指向的東西,這個東西有這些特點:它的型別是 p指向的型別,它所佔用的地址是p所指向的地址。 

3.指標算數運算

 C的指標的算術運算只侷限於兩種形式。

第一種形式是:   指標+-整數  標準定義這種形式只能用於指向陣列中某個元素的指標。對一個指標加1使它指向陣列中的下一個元素,加5使它向右移動5個元素的位置,依次類推。把一個指標減去3使它向左移動3個元素的位置。

第二種型別的指標運算具有如下的形式:   指標—指標  兩個指標相減的結果的型別是ptrdiff_t,它是一種有符合整數型別。減法運算的值是兩個指標在記憶體中的距離(以陣列元素的長度為單位,而不是以位元組為單位),因為減法運算的結果將除以陣列元素型別的長度。

4.常量指標和指標常量

指向常量的指標

constint *pa;
int const *pa;
兩者等價。因為指向常量的指標有時候會指向常量,所以它具有這個性質:“不能靠解引用改變它指向的物件的值”,以此保護它所指向的常量的常量性:
*pa =d; // 不可行(d是已經宣告過的整型)

但指標本身的值是可變的:
pa=& d; // 可行(d是已經宣告過的整型)

而且指向常量的指標有時候也會指向變數,如下:
int t,u;
const int *pa;
pa =&t; //可行,指向變數t
pa =&u; //也可行,指向變數u

我們可以把它理解成:“為了指向常量而發明的指標”,這樣比較貼切。

常量指標:

int*const pa =&n; // n是之前已經宣告過的整型變數,注意必須是變數,理由見下

“常量指標”即指標本身的值是常量,但“能靠解引用改變它指向的物件的值”,如下:
pa=&d; // 不可行(d是已經宣告過的整型)
*pa =d; // 可行(d是已經宣告過的整型)

因為常量指標也是一種const常量,所以它同樣必須在第一次宣告時就初始化,不過它的初始值縮小為只能是變數(的地址),因為只有變數才能確保以後能靠解引用而改變它指向的物件的值。這使得常量指標不象一般的const常量,用變數或常量初始化都可以。
也就是說,常量指標反而總是指向變數的。

5.空指標及其使用

空指標常量,ANSI規定:<stdio.h>規定預處理巨集NULL 為空指標常量,通常#define NULL 0或(void *)0

誤區:有的機器不同型別的指標使用不同的內部表示,例如將字元指標的空指標常量定義為#define NULL ((char *)0),這樣的NULL定義對於接受字元指標的函式沒有問題,但對於其他型別的指標仍然需要進行顯示的轉換,本來合法的構造可能會失敗,例如FILE *fp=NULL;

注意1:NULL只能用做指標,非指標變數中不能用NULL

注意2:執行時的整數0轉化為指標不一定是空指標,只有常量整數0才能保證空指標

6.void指標

1.void指標是一種特別的指標
   void *vp
  //
說它特別是因為它沒有型別
  //
或者說這個型別不能判斷出指向物件的長度

2.任何指標都可以賦值給void指標
  type *p;
  vp=p;
  //
不需轉換
  //
只獲得變數/物件地址而不獲得大小

3.void指標賦值給其他型別的指標時都要進行轉換
   type *p=(type*)vp;
   //
轉換型別也就是獲得指向變數/物件大小:http://icoding.spaces.live.com/blog/cns!209684E38D520BA6!130.entry

4.void指標不能復引用
  *vp//
錯誤因為void指標只知道,指向變數/物件的起始地址而不知道指向變數/物件的大小(佔幾個位元組)所以無法正確引用

5.void指標不能參與指標運算,除非進行轉換
   (type*)vp++;
  //vp==vp+sizeof(type)

因此,void*的作用大致如下:

1. 傳參:通用型別

可以作為函式模板,連結串列等引數的通用引數。在使用時,只需要強制型別轉換就可以。

2.強制型別轉換

有時候由於過載等的干擾,導致需要轉換成void*,來進行取地址。

例如,(void*)obj.member,就可以取到member的地址;直接&(obj.member)取到的實際上是obj的開始地址。

3.指向0的地址

(void *)0,指向全是0的地址,相當於NULL

void型別顯式轉換為void型別表示式,用於避免一些程式碼靜態檢查工具的警告。