C語言中的精華是什么,答曰指針,這也是C語言中唯一的難點。
C是對底層操作非常方便的語言,而底層操作中用到最多的就是指針,以后從事嵌入式開發(fā)的朋友們,指針將陪伴我們終身。
本文將從八個常見的方面來透視C語言中的指針,當然,還有其他沒有具體提到的方面,像指針表達式、指針安全等問題,以后有機會我再慢慢補充。
還是那句老話,重要的是實踐,多寫代碼,才是學好C語言的關鍵。
1.指針類型分析
分析指針,可以從變量名處起,根據(jù)運算符優(yōu)先級結合,一步一步分析.
int p;//這是一個普通的整型變量
int *p;//首先從P處開始,先與*結合,所以說明P是一個指針,然后再與int結合,說明指針所指向的內容的類型為int 型.所以 P是一個返回整型數(shù)據(jù)的指針
int p[3]; //首先從P處開始,先與[]結合,說明P 是一個數(shù)組,然后與int結合,說明數(shù)組里的元素是整型的,所以 P是一個由整型數(shù)據(jù)組成的數(shù)組
int *p[3]; //首先從P處開始,先與[]結合,因為其優(yōu)先級比*高,所以P是一個數(shù)組,然后再與*結合,說明數(shù)組里的元素是指針類型,然后再與 int結合,說明指針所指向的內容的類型是整型的,所以是一個由返回整型數(shù)據(jù)的指針所組成的數(shù)組
int (*p)[3]; //首先從P處開始,先與*結合,說明P是一個指針然后再與[]結合(與"()"這步可以忽略,只是為了改變優(yōu)先級),說明指針所指向的內容是一個數(shù)組,然后再與int 結合,說明數(shù)組里的元素是整型的.所以P是一個指向由整型數(shù)據(jù)組成的數(shù)組的指針
int **p; //首先從 P開始,先與*結合,說明P是一個指針,然后再與*結合,說明指針所指向的元素是指針,然后再與 int結合,說明該指針所指向的元素是整型數(shù)據(jù). 所以P是一個返回指向整型數(shù)據(jù)的指針的指針
int p(int);//從P處起,先與()結合,說明P是一個函數(shù),然后進入()里分析,說明該函數(shù)有一個整型變量的參數(shù)然后再與外面的int 結合,說明函數(shù)的返回值是一個整型數(shù)據(jù).所以P是一個有整型參數(shù)且返回類型為整型的函數(shù)
int (*p)(int); //從P處開始,先與指針結合,說明P是一個指針,然后與()結合,說明指針指向的是一個函數(shù),然后再與()里的int 結合,說明函數(shù)有一個int 型的參數(shù),再與最外層的int 結合,說明函數(shù)的返回類型是整型,所以P是一個指向有一個整型參數(shù)且返回類型為整型的函數(shù)的指針
int *(*p(int))[3]; //從 P開始,先與()結合,說明P是一個函數(shù),然后進入()里面,與int結合,說明函數(shù)有一個整型變量參數(shù),然后再與外面的*結合,說明函數(shù)返回的是一個指針,,然后到最外面一層,先與[]結合,說明返回的指針指向的是一個數(shù)組,然后再與*結合,說明數(shù)組里的元素是指針,然后再與int 結合,說明指針指向的內容是整型數(shù)據(jù).所以P是一個參數(shù)為一個整數(shù)且返回一個指向由整型指針變量組成的數(shù)組的指針變量的函數(shù)
2.指針分析
指針是一個特殊的變量,它里面存儲的數(shù)值被解釋成為內存里的一個地址。
要搞清一個指針需要搞清指針的四方面的內容:指針的類型、指針所指向的類型、指針的值或者叫指針所指向的內存區(qū)、指針本身所占據(jù)的內存區(qū)。
指針的類型:把指針聲明語句里的指針名字去掉,剩下的部分就是這個指針的類型
指針所指向的類型:把指針聲明語句中的指針名字和名字左邊的指針聲明符*去掉,剩下的就是指針所指向的類型(在指針的算術運算中,指針所指向的類型有很大的作用)
指針所指向的內存區(qū):從指針的值所代表的那個內存地址開始,長度為sizeof(指針所指向的類型)的一片內存區(qū)。(一個指針指向了某塊內存區(qū)域,就相當于說該指針的值是這塊內存區(qū)域的首地址)
指針本身所占據(jù)的內存區(qū):用函數(shù)sizeof(指針的類型)可以測出指針本身所占據(jù)的內存區(qū)(在 32位平臺里,指針本身占據(jù)了 4個字節(jié)的長度)
3.指針的算術運算
指針和整數(shù)進行加減:一個指針 ptrold加(減)一個整數(shù) n后,結果是一個新的指針ptrnew,ptrnew 的類型和 ptrold 的類型相同,ptrnew 所指向的類型和 ptrold所指向的類型也相同,ptrnew的值將比 ptrold 的值增加(減少)了n乘sizeof(ptrold所指向的類型)個字節(jié)。
指針和指針進行加減:兩個指針不能進行加法運算,這是非法操作;兩個指針可以進行減法操作,但必須類型相同,一般用在數(shù)組方面。
4. 運算符&和*
&是取地址運算符,*是間接運算符。
&a的運算結果是一個指針,指針的類型是a的類型加個*,指針所指向的類型是a的類型,指針所指向的地址嘛,那就是a的地址。
*p的運算結果就五花八門了,總之*p 的結果是 p 所指向的東西,這個東西有這些特點:它的類型是 p指向的類型,它所占用的地址是p所指向的地址。
5. 數(shù)組和指針的關系
數(shù)組的數(shù)組名其實可以看作一個指針。
聲明了一個數(shù)組 TYPE array[n],則數(shù)組名稱array就有了兩重含義:
第一,它代表整個數(shù)組,它的類型是 TYPE[n];
第二 ,它是一個常量指針,該指針的類型是TYPE*,該指針指向的類型是 TYPE,也就是數(shù)組單元的類型,該指針指向的內存區(qū)就是數(shù)組第0號單元,該指針自己占有單獨的內存區(qū),注意它和數(shù)組第0號單元占據(jù)的內存區(qū)是不同的。該指針的值是不能修改的,即類似 array++的表達式是錯誤的。
6. 指針和結構類型的關系
假設我們定義了一個結構體,struct MyStruct{inta;int b;int c;};
同時定義結構體的結構對象并初始化,struct MyStructss={20,30,40};
那么我們如何通過指針ptr 來訪問 ss的三個成員變量呢?
答案就是,我們先定義一個指向結構對象 ss的指針,struct MyStruct *ptr=&ss; 然后,使用指向運算符->便可實現(xiàn)對結構對象ss成員的訪問。
ptr->a; //或者可以這們(*ptr).a,建議使用前者
ptr->b;
ptr->c;
7. 指針和函數(shù)的關系
可以把一個指針聲明成為一個指向函數(shù)的指針,從而通過函數(shù)指針調用函數(shù)。讓我們舉一個例子來說明以下吧。
int fun(char *,int);
int (*pfun)(char *,int);
pfun=fun;
int a=(*pfun)("abcdefg",7);
例中,定義了一個指向函數(shù)fun的指針pfun,把pfun作為函數(shù)的形參。把指針表達式作為實參,從而實現(xiàn)了對函數(shù)fun的調用。
8. 指針類型轉換
當我們初始化一個指針或給一個指針賦值時,賦值號的左邊是一個指針,賦值號的右邊是一個指針表達式,這就要求兩邊的類型一致,所指向的類型也一致,如果不一致的話,需要進行強制類型轉換。語法格式是:(TYPE *)p;
這樣強制類型轉換的結果是一個新指針,該新指針的類型是TYPE *,它指向的類型是TYPE,它指向的地址就是原指針指向的地址。要注意的是,原來的指針p的一切屬性都沒有被修改。
另外,一個函數(shù)如果使用了指針作為形參, 那么在函數(shù)調用語句的實參和形參的結合過程中,也必須保證類型一致 ,否則需要強制轉換。
|