C語言指針的用法
- C語言
- 關注:1.05W次
學習 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]='