C指標的用法總結—轉載於西郵一個大神

來源:互聯網
上載者:User

轉載地址http://blog.renren.com/share/425935703/14545505748?from=0101010202&ref=hotnewsfeed&sfet=102&fin=48&fid=20050008831&ff_id=425935703

char *p, **p, ***p;

char p[],p[][],p[][][];

char *p[],*p[][],**p[],**p[][],*(*p)[],(**p)[],(**p)[][];

 void (*pFun)(int i);

大神們看到這些東西腦袋裡一定像藍天白雲一樣清晰明了。。。

(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;

第二組: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]='\0';}

return (p);

}

        這裡使用malloc分配了記憶體(在堆中)在函數返回後依然有效。

        這裡還沒完,因為有一天我使用了下面的代碼:

char * fun(void)

{

char *p="abc";

return (p);

}

發現雖然p定義為局部變數,但返回也是正確的。還記得上面講到的常量字串儲存位置嗎?指標p實際指向了待用資料區,這裡的char *p相當於const char *p,這樣的資料在函數返回後是不會被立刻回收掉的。

        指標作為參數主要用於修改指標指向的資料內容,但修改指標的值無效,

char * fun(char *p)

{

char i='a';

p=(char *)malloc(5);

p[0]=i;

return p;

}

        因為傳遞的是一個指標副本(形參指標和實參指標的值相同,但不是同一個指標),不會影響調用方的實參值。(詭異的vs2012貌似可以通過形參將實參的值改掉!不過還是不要冒這個險為好了)。

(5)關於const修飾符

        const修飾符用於指標時也非常糾結。

        首先要分清const char *p和char* const p。

        const char *p是指向const對象的指標,即對象是唯讀,而指標不是。使用const對象的指標要注意兩點:

一是不能將其賦值給非const對象的指標,

如:

const char* p;

char *p1=p; //不允許的

        當然,直接使用非const指標指向const對象也是不合法的,

如:

const char c;

char *p1=&c;//不允許的,

        這是要避免通過上述p1改變const對象的值。

        二是可以將非const對象的地址賦值給指向const對象的指標,但是試圖使用這個指標改變變數的值是非法的,

如:

char c='a'; 

const char* p=&c;//允許的

*p='b';//不允許的

        char* const p是const指標,即指向對象可編輯,但指標本身不可修改,這一點類似於數組。

如:

char c='a'; 

char* const p=&c;

*p='b';//允許的

p++;//不允許的

        區分兩者的方法是,看const是否靠近指標名,如果是則為const指標,否則為const對象。這個助記方法的前提是char要和*號靠在一起,因為const char* p=char const *p。

另外,還有const char* const p,自然是指向const對象的const指標了。

(6)關於指標函數

        首先注意指標函數和函數指標的區別,前者是指“返回指標的函數”,這在上文中有提到,而後者是指“指向函數的指標”。

        函數指標的定義方法為,將“函數名”替換為“(*函數指標名)”,

如:

        指向一個聲明為void fun(int a)的函數指標可以定義為 void (*pFun)(int a)或void (*pFun)(int ),注意這裡函數指標pFun只能指向和fun有相同傳回型別(void)和參數類型(int)的一類函數,另外定義中()也不是擺設,去掉()會被看做是傳回值為void*類型的函式宣告。舉個例子:

void fun(int a)

{

cout<<a<<endl;

int main()

{

void (*pFun)(int);

pFun=&fun;  //(1)

*(pFun)(1);   //(2)

}

        事實上,上式中的(1)(2)行做如下幾種替換也是正確的:

a、pFun=fun; 

   pFun(1); 

b、pFun=&fun; 

   pFun(1); 

c、pFun=fun; 

  *(pFun)(1); 

        如果有什麼疑問的話,可以接著嘗試用如下方式直接調用函數fun:

(*fun)(1);

        啟動並執行結果也是正確的!這要怎麼解釋呢?

        其實,fun不僅僅作為函數名,它同pFun一樣也是一個函數指標,只不過是函數指標常量。為了書寫方便,c語言開發人員允許將函數指標調用直接寫成類似fun()的形式,同樣函數指標變數賦值也可以寫成類似pFun=&fun的形式。值得注意的是,函式宣告的格式還是比較嚴格的,如:

void fun(int );    //不能寫成void (*fun)(int )。

        同樣,
void (*pFun)(int );   //不能寫成void pFun(int )。

        為了方便,我們還可以定義函數指標類型,針對上述例子的定義方法如下:

typedef  void (*PFUN)(int);

        這樣一來我們就可以用

PFUN pFun;

來聲明一個函數指標了。

        有了函數指標之後,函數的參數也可以設為某一類函數類型。

如:

typedef  void (*PFUN)(int);

void fun(int a)

{

cout<<a<<endl;

}

void topfun(PFUN f1, int a)

{

f1(a);

int main()

{

topfun(fun,1);

return 1;

}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.