本文**:
kenshin cui's blog
指標是c語言的精髓,但是很多初學者往往對於指標的概念並不深刻,以至於學完之後隨著時間的推移越來越模糊,感覺指標難以掌握,本文通過簡單的例子試圖將指標解釋清楚,今天的重點有幾個方面:
什麼是指標
陣列和指標
函式指標
存放變數位址的變數我們稱之為「指標變數」,簡單的說變數p中儲存的是變數a的位址,那麼p就可以稱為是指標變數,或者說p指向a。當我們訪問a變數的時候其實是程式先根據a取得a對應的位址,再到這個位址對應的儲存空間中拿到a的值,這種方式我們稱之為「直接引用」;而當我們通過p取得a的時候首先要先根據p轉換成p對應的儲存位址,再根據這個位址到其對應的儲存空間中拿到儲存內容,它的內容其實就是a的位址,然後根據這個位址到對應的儲存空間中取得對應的內容,這個內容就是a的值,這種通過p找到a對應位址再取值的方式成為「間接引用」。這裡以**形式列出a和p的儲存以幫助大家理解上面說的內容:
接下來,看一下指標的賦值
//需要說明兩點:// main.c
// point
//// created by kenshin cui on 14-7-05.
//#include
int
main(
int
argc,
const char
* argv)
a.int *p;中的*只是表示p變數是乙個指標變數;而列印*p的時候,*p中的*是操作符,表示p指標指向的變數的儲存空間(當前儲存就是1),同時我們也看到了*p==a;修改了*p也就是修改了p指向的儲存空間的內容,也就修改了a,所以第二次列印a=2;
b.指標所指向的型別必須和定義指標時宣告的型別相同;上面指標q定義成了int型而指向了char型,結果輸出*q列印出了2049,具體原因見下圖(假設在16位編譯器下,指標長度為2位元組)
由於區域性變數是儲存在棧裡面的,所以先儲存b再儲存a、p,當列印*p的時候,其實就是以p指向的位址對應的空間開始取兩個位元組的資料(因為定義p的時候它指向的是int型,在16位編譯器下int型別的長度為2),剛好定義的b和c空間連續,所以就取到b的其中乙個位元組,最後*p二進位制儲存為「0000100000000001」(見上圖黃色背景內容),十進位制表示就是2049;
c.指標變數占用的空間和它所指向的變數型別無關,只跟編譯器位數有關(準確的說只跟定址方式有關);
//從上面的例子我們可以得出如下結論:// main.c
// point
//// created by kenshin cui on 14-7-05.
//#include
void
changevalue(
int
a)void
changevalue2(
int
*p)int
main(
int
argc,
const char
* argv) ;
int
*p=&a[0];
//等價於:*p=a;
printf(
"len=%lu\n"
,sizeof
(int
));//取得int長度為2
//指標加1代表位址向後挪動所指向型別的長度位(這裡型別是int,長度為2)
//也就是說p指向a[0],p+1指向a[1],以此類推,所以我們通過指標也可以取出陣列元素
for(
int
i=0;i<3;++i)
/*輸出結果:
a[0]=1
a[1]=2
a[2]=3
*/changevalue(p);
//等價於:changevalue(a)
for(
int
i=0;i<3;++i)
/*輸出結果:
a[0]=2
a[1]=2
a[2]=3
*/changevalue2(a);
//等價於:changevalue2(p)
for(
int
i=0;i<3;++i)
/*輸出結果:
a[0]=3
a[1]=2
a[2]=3
*/return
0;}
陣列名a==&a[0]==*p;
如果p指向乙個陣列,那麼p+1指向陣列的下乙個元素,同時注意p+1移動的長度並不固定,具體需要根據p指向的資料型別而定;
指標可以寫成p++形式,但是陣列名不可以,因為陣列名是常量
不管函式的形參為陣列還是指標,實參都可以使用陣列名或指標;
由於在c語言中字串就是字元陣列,下面不妨看一下字串和陣列的關係:
//以上**中注釋基本已經很清楚了,這裡需要指出是為什麼printf(a)能夠直接輸出字串呢?// main.c
// point
//// created by kenshin on 14-7-05.
//#include
int
main(
int
argc,
const char
* argv)
我們看一下printf()的定義:int printf(const char * __restrict, ...) __printflike(1, 2);
在弄清函式指標的問題之前,我們不妨先來看一下返回指標型別資料的函式,畢竟指標型別也是c語言的資料型別,下面以乙個字串轉換為大寫字元的程式為例,在這個例子中不僅可以看到返回值為指標型別的函式同時還可以看到前面說到的指標移動操作:
//大家都是知道函式只能有乙個返回值,如果需要返回多個值,怎麼辦,其實很簡單,只要將指標作為函式引數傳遞就可以了,在下面的例子中我們再次看到指標作為引數進行傳遞。// main.c
// point
//// created by kenshin cui on 14-06-28.
//#include
char
* toupper(
char
*a) }
return b;}
int
main(
int
argc,
const char
* argv)
//函式也是在記憶體中儲存的,當然函式也有乙個起始位址(事實上函式名就是函式的起始位址),這裡同樣需要弄清函式指標的關係。函式指標定義的形式:返回值型別 (*指標變數名)(形參1,形參2),拿到函式指標其實我們就相當於拿到了這個函式,函式的操作都可以通過指標來完成,而且通過前面的例子可以看到指標作為c語言的資料型別,可以作為引數、作為返回值,那麼當然函式指標同樣可以作為函式的引數和返回值:// main.c
// point
//// created by kenshin cui on 14-6-28.
//#include
int
operate(
int
a,int
b,int
*c)int
main(
int
argc,
const char
* argv)
//函式指標可以作為函式引數進行傳遞,實在太強大了,是不是想起了c#中的委託?記得c#書籍中經常提到委託類似於函式指標,其實說的就是上面的情況。需要注意的是,普通的指標可以寫成p++進行移動,而函式指標寫成p++並沒有意義。// main.c
// point
//// created by kenshin cui on 14-6-28.
//#include
int
sum(
int
a,int
b)int
sub(
int
a,int
b)//函式指標作為引數進行傳遞
int
operate(
int
a,int
b,int
(*p)(
int,
int))
int
main(
int
argc,
const char
* argv)
IOS開發系列 C語言之指標
指標是c語言的精髓,但是很多初學者往往對於指標的概念並不深刻,以至於學完之後隨著時間的推移越來越模糊,感覺指標難以掌握,本文通過簡單的例子試圖將指標解釋清楚,今天的重點有幾個方面 什麼是指標 陣列和指標 函式指標 存放變數位址的變數我們稱之為 指標變數 簡單的說變數p中儲存的是變數a的位址,那麼p就...
iOS開發系列 C語言之預處理
ios開發系列 c語言之預處理 大家都知道乙個c程式的執行包括編譯和鏈結兩個階段,其實在編譯之前預處理器首先要進行預處理操作,將處理完產生的乙個新的原始檔進行編譯。由於預處理指令是在編譯之前就進行了,因此很多時候它要比在程式執行時進行操作效率高。在c語言中包括三類預處理指令,今天將一一介紹 巨集定義...
iOS開發系列 C語言之預處理
大家都知道乙個c程式的執行包括編譯和鏈結兩個階段,其實在編譯之前預處理器首先要進行預處理操作,將處理完產生的乙個新的原始檔進行編譯。由於預處理指令是在編譯之前就進行了,因此很多時候它要比在程式執行時進行操作效率高。在c語言中包括三類預處理指令,今天將一一介紹 巨集定義條件編譯 檔案包含 對於程式中經...