C語言指標的用法
學習 C 語言的指標既簡單又有趣。通過指標,可以簡化一些 C 程式設計任務的執行,還有一些任務,如動態記憶體分配,沒有指標是無法執行的。所以,想要成為一名優秀的 C 程式設計師,學習指標是很有必要的。下面是相關的知識,歡迎閱讀。
(1)關於指標與陣列的儲存a、指標和陣列在記憶體中的儲存形式
陣列p[N]建立時,對應著記憶體中一個數組空間的分配,其地址和容量在陣列生命週期內一般不可改變。陣列名p本身是一個常量,即分配陣列空間的地址值,這個值在編譯時會替換成一個常數,在執行時沒有任何記憶體空間來儲存這個值,它和陣列長度一起存在於程式碼中(應該是符號表中),在連結時已經制定好了;而指標*p建立時,對應記憶體中這個指標變數的空間分配,至於這個空間內填什麼值即這個指標變數的值是多少,要看它在程式中被如何初始化,這也決定了指標指向哪一塊記憶體地址。
b、指標和陣列的賦值與初始化
根據上文,一般情況下,陣列的地址不能修改,內容可以修改;而指標的內容可以修改,指標指向的內容也可以修改,但這之前要為指標初始化。
如:
int p[5];
p=p+1; 是不允許的
而p[0]=1; 是可以的;
//
int *p;
p=p+1; 是允許的
p[0]=1; 是不允許的,因為指標沒有初始化;
//
int i;
int *p=&i;
p[0]=1; 是允許的;
對於字元指標還有比較特殊的情況。
如:
char * p="abc";
p[0]='d'; 是不允許的
為什麼初始化了的字元指標不能改變其指向的內容呢?這是因為p指向的是“常量”字串,字串"abc"實際是儲存在程式的靜態儲存區的,因此內容不能改變。這裡常量字串的地址確定在先,將指標指向其在後。
而
char p[]="abc";
p[0]='d'; 是允許的
這是因為,這個初始化實際上是把常量直接賦值給陣列,即寫到為陣列分配的記憶體空間。這裡陣列記憶體分配在先,賦值在後。
(2)關於一些表示式的含義
char *p, **p, ***p;
char p[],p[][],p[][][];
char *p[],*p[][],**p[],**p[][],*(*p)[],(**p)[],(**p)[][];
能清晰地知道以上表達式的含義嗎?(知道的去死!)
第一組:char *p, **p, ***p;
分別為char指標;char*指標,即指向char*型別資料地址的指標;char**指標,即指向char**型別資料的指標;他們都是佔4位元組空間的指標。
如:
char c='a';
char *p=&c;
char **p1=&p;
char ***p2=&p1;
cout<<***p2<<endl;< p="">
第二組:char p[],p[][],p[][][];
分別為一維,二維和三維char型陣列,即陣列,陣列的陣列,<陣列的陣列>的陣列。可以如下的方式進行初始化:
char pp[3]="ab";
char pp1[3][3]={"ab"};
char pp2[3][3][3]={{"ab"}};
現在我們嘗試使用第二組三個陣列名對應為第一組三個指標賦值,直接賦值的結果如下:
p=pp; //正確
p1=pp1; //錯誤
p2=pp2; //錯誤
為什麼p1和p2的賦值會出錯呢?原因是陣列名為給指標賦值的規則不是遞迴的,即陣列的陣列可以為陣列的指標賦值,而不可以為指標的指標賦值。這裡先了解到這個抽象的規則,下面講完第三組表示式,等我們知道陣列的指標和指標的陣列如何書寫後再對這一問題舉例說明。
第三組:char *p[],*p[][],**p[],**p[][],*(*p)[],(**p)[],(**p)[][];
這一類表示式的解析方法如下:
首先把整個表示式分為三部分,
資料型別和星號部分+p或括號內內容部分+中括號部分
如:char *(*p)[]分為char* ,(*p) 和 []
“char*”表示最內層儲存的資料型別“(*p)”表示最外層指標“[]”表示中間層陣列(維數=中括號數目),因此上式表示一個一維陣列的指標p,陣列中的元素的資料型別是指標char*。同理,char (**p)[][]表示,一個二維陣列的指標的指標,陣列元素的資料型別是char。這裡如果表示式中間沒有括號(如**p[]),則實際上是一個數組,如果最右沒有中括號(如**p),則實際上是一個指標。下面通過賦值表示式來理解這些表示式的含義:
char c='a';
char *pc=&c;
char *p[3],*p1[3][3],**p2[3],**p3[3][3],*(*p4)[3],(**p5)[3],(**p6)[3][3],(*(*p7))[3];
p[1]=pc;
p1[0][0]=pc;
p2[0]=&pc;
p3[0][0]=&pc;
(*p4)[0]=pc;
(**p5)[0]=c;
(**p6)[0][0]=c;
(**p7)[0]=c;
注意,(*(*p7))[3]和(**p5)[3]是等價的。
這裡再繼續上一小節講一下陣列名給指標賦值的問題。
事實上可以對等賦值的陣列和指標關係如下(——>表示“賦值給”):
陣列——>指標 : p[]——>*p
指標的陣列——>指標的指標 : *p[]——>**p
指標的指標的陣列的——>指標的指標的指標 : **p[]——>***p
。。。。。。
或
陣列的陣列——>陣列的指標 : p[][]——>(*p)[]
陣列的陣列的陣列的——>陣列的陣列的指標 : p[][][]——>(*p)[][]
總之,最外層的陣列可以轉換指標,往內層不遞迴。
(3)關於上述表示式的長度
求一個表示式的“長度”,首先分清表示式實際表示的是一個數組還是一個指標;如果是指標,則長度為4byte;如果為陣列則要計算實際儲存的總元素個數和元素的資料型別。另外要注意要求的是陣列元素個數還是陣列總位元組數;
如:
*(*p)[3][3]
由上文可知上式表示一個指標,因此長度為4byte;而
**p3[3][3]
表示一個二維陣列,陣列元素型別為指標的指標,因此長度為3*3*4=36;
注意,標準C中sizeof函式求得的是總位元組數而非陣列長度。
(4)關於函式的指標返回值和指標引數
指標作為返回值要注意的地方是不要返回區域性資料的指標。
如:
char * fun(void)
{
char i='a';
return (&i);
}
呼叫函式fun得不到值'a',原因是函式返回後,區域性資料(在棧中)被析構,資料記憶體被回收,指標指向的資料沒有意義;
可以改為:
char * fun(void)
{
char i='a';
char *p=(char *)malloc(5);
If(p!=NULL) {p[0]=i, p[1]='