現在每篇部落格都要求有摘要,但是如果我就這樣直接接著上一篇的內容開始講的話可能第一次看我部落格的人都不知道我在說啥,所以我還是把前一篇部落格的第一段作為摘要搬過來吧。
懂得C語言的人都知道,C語言之所以強大,以及其自由性,絕大部分體現在其靈活的指標運用上。因此,說指標是c語言的靈魂,一點都不為過。所以從我的標題加了個(一)也可以看出指標的重要性,我儘可能的向大家交代清楚我對於指標的理解。所以在講解的過程中我儘可能的用代碼加文字的描述方式,通過代碼的分析來加深我們對於指標的理解,我給出的都是完整的代碼,所以讀者可以在看的過程中直接copy下去即可運行,希望下面的講解能夠對你有所協助。
在此也特地強調下,如果以後出現類似的情況時,我部落格的第一段均作為摘要。如果已經在前面的部落格中看過摘要的,那麼重複的摘要部分可跳過不讀,直接進入本文。
接著上一篇的指標部分,我們接下來看看數組的指標和指向數組的指標變數。數組的指標就是數組的起始地址,數組元素的指標是數組元素的地址。對於一個數組元素的引用我們通常可以使用兩種方法:
1、下標法,如a[8]。
2、指標法。
其中使用指標法的優點是使得目標程式占記憶體少、運行速度快,從而使得其品質更高。為什麼說指標具有這樣的優點呢,我想還是有必要在此給出點我解釋,因為指標在32位機器下佔用4個位元組,如果函數傳輸一個佔用記憶體很大的對象例如:int a[2000],顯然用指標引用傳送簡單,節省了記憶體,也節省了用於複製對象的時間;如果我們用下標法來引用數組,還得去取數組的起始地址,通過base + offset再轉換為直接定址,比指標多了操作。
從以上可以看出,指標能力很強,能完成許多事情,C的精髓就在於指標,使得C能接近ASM的效率。所以我們在寫編寫程式的時候有必要充分利用指標的優點,編寫出高效的C語言代碼。
下面來看看一個代碼:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a[8];
int *p;
//***************************用下標法列印a數組**************************//
printf("\n***************************用下標法列印a數組**************************\n");
for(int i=0;i<8;i++)
{
a[i]=i;
printf("a[%d]=%d\t",i,a[i]);
}
printf("\n****************************end end end******************************\n");
//****************************end end end******************************//
//***************************使用指標變數列印**************************//
printf("\n***************************使用指標變數列印**************************\n");
p=a;
for(int j=0;j<8;j++)
{
printf("p%d=%d\t",j,*p++);
}
printf("\n****************************end end end******************************\n");
//****************************end end end******************************//
printf("\n");
//************************用數組名指標運算列印*************************//
printf("\n************************用數組名指標運算列印*************************\n");
for(int k=0;k<8;k++)
{
printf("a[%d]=%d\t",k,*(a+k));
}
printf("\n****************************end end end******************************\n");
//****************************end end end******************************//
//****************************列印二維數組b的值地址********************//
printf("\n***************************列印二維數組b的值及地址*******************\n");
int b[4][4];
for(int n=0;n<4;n++)
{
for(int m=0;m<4;m++)
{
b[n][m]=n*m;
printf("%d\t",b[n][m]);
printf("%d\t",&b[n][m]);
}
printf("\n");
}
printf("\n****************************end end end******************************\n");
//****************************end end end******************************//
int *pp=&b[0][0];
int **ppp=&pp;
//*********************二維數組b的地址、以及pp和*ppp的值****************//
printf("\n*********************二維數組b的地址、以及pp和*ppp的值****************\n");
printf("\n&b[0][0]=%d\tpp=%d\t*ppp=%d\n",&b[0][0],pp,*ppp);
printf("\n****************************end end end******************************\n");
//****************************end end end******************************//
//*****************二維數組b[0][0]、以及*pp和**ppp的值******************//
printf("\n*****************二維數組b[0][0]、以及*pp和**ppp的值******************\n");
printf("\nb[0][0]=%d\t*pp=%d\t**ppp=%d\n",b[0][0],*pp,**ppp);
printf("\n****************************end end end******************************\n");
//****************************end end end******************************//
//***************************使用指標變數列印**************************//
printf("\n***************************使用指標變數列印**************************\n");
for(pp=&b[0][0];pp<(&b[0][0]+16);pp++)
printf("%d\t",*pp);
printf("\n****************************end end end******************************\n");
//****************************end end end******************************//
printf("\n&pp=%d\t&ppp=%d\n",&pp,&ppp);
printf("\nppp=%d\t*ppp=%d\n",ppp,*ppp);
printf("%d\t",*(*ppp-1));
return 0;
}
在寫上面的代碼時,我加上了很多的注釋,和列印說明語句,使得代碼看起來不怎麼美觀,但是它絲毫不會影響我們對於代碼的閱讀,下面先讓我們來看看運行結果後再來對齊進行分析。
上面的圖片可能有點偏大。因為圖片看起來效果更好些,所以我還是把圖片傳上來的同時也把列印結果複製了一份如下,如果圖片因為網路原因打不開就看下面的運行結果:
***************************用下標法列印a數組**************************
a[0]=0 a[1]=1 a[2]=2 a[3]=3 a[4]=4 a[5]=5 a[6]=6 a[7]=7
****************************end end end******************************
***************************使用指標變數列印**************************
p0=0 p1=1 p2=2 p3=3 p4=4 p5=5 p6=6 p7=7
****************************end end end******************************
************************用數組名指標運算列印*************************
a[0]=0 a[1]=1 a[2]=2 a[3]=3 a[4]=4 a[5]=5 a[6]=6 a[7]=7
****************************end end end******************************
***************************列印二維數組b的值及地址*******************
0 1244944 0 1244948 0 1244952 0 1244956
0 1244960 1 1244964 2 1244968 3 1244972
0 1244976 2 1244980 4 1244984 6 1244988
0 1244992 3 1244996 6 1245000 9 1245004
****************************end end end******************************
*********************二維數組b的地址、以及pp和*ppp的值****************
&b[0][0]=1244944 pp=1244944 *ppp=1244944
****************************end end end******************************
*****************二維數組b[0][0]、以及*pp和**ppp的值******************
b[0][0]=0 *pp=0 **ppp=0
****************************end end end******************************
***************************使用指標變數列印**************************
0 0 0 0 0 1 2 3 0 2
4 6 0 3 6 9
****************************end end end******************************
&pp=1244932 &ppp=1244928
ppp=1244932 *ppp=1245008
9 Press any key to continue
首先來看看我們使用的三種列印一維數組a的方法,都成功的對a數組中的每個元素進行了列印,接下類是一個列印二維數組b的過程,在列印數組b中每個元素的同時我們也列印出了它相應的地址,細心的讀者可能發信地址間的規律,因為我們聲明的是int型,所以每個元素佔用4個位元組,相鄰元素間的地址之差為4。
接下來我們使用了一個指標pp和一個指向指標的指標ppp,在使用指標的指標ppp的過程中要尤其注意它的使用。通過列印語句我們列印出了&b[0][0],pp,*ppp,其都具有相同的結果,都為二維數組b[0][0]的地址,所以接下來列印的b[0][0],*pp,**ppp均為b[0][0]的值,接下來我們採用指標的方法來成功的列印了二維數組b。
在接下來我們列印出了指標pp、雙指標ppp的地址,同時也列印了ppp和*ppp的值,注意了*ppp的值和最後一次列印的數組元素的地址的關係,為什麼會出現這樣的結果呢,因為我們前面使用了一句int **ppp=&pp;,使得*ppp和pp指向的是同一個儲存空間,其地址為&pp=1244932 ,所以在改變pp的值得時候,*ppp的值也在跟隨其改變。所以細心的讀者可能發現了在最後一句列印語句printf("%d\t",*(*ppp-1));中,我們使用了*(*ppp-1)才能成功的列印出二維數組b的最後一個最後一個元素。
如何採用二維指標類列印數組呢,請看下面的代碼:
#include <stdio.h>
int main()
{
//****************************列印二維數組b的值地址********************//
printf("\n***************************列印二維數組b的值及地址*******************\n");
int b[4][4];
for(int n=0;n<4;n++)
{
for(int m=0;m<4;m++)
{
b[n][m]=n*m;
printf("%d\t",b[n][m]);
printf("%d\t",&b[n][m]);
}
printf("\n");
}
printf("\n****************************end end end******************************\n");
//****************************end end end******************************//
int *pp=&b[0][0];
int **ppp=&pp;
//****************************使用二維指標的列印數組b*******************//
printf("\n***************************使用二維指標的列印數組b*******************\n");
for(*ppp;*ppp<(&b[0][0]+16);(*ppp)++)
printf("%d\t",**ppp);
printf("\n****************************end end end******************************\n");
//****************************end end end******************************//
return 0;
}
運行結果如下:
注意代碼中我們的紅色標記部分,很多人在採用二維指標**ppp進行列印的時候最容易出錯的地方,很多人使用的是如下方式:
for(*ppp;*ppp<(&b[0][0]+16);*ppp++)
看似沒有問題,似乎能得到正確的結果,但是我們仔細分析就會發現其中的問題所在,因為++的優先順序高於*,所以首先進行的是ppp++運算,然後才是*ppp,這樣的話就出現我們前面所講的野指標的問題了。所以在調用printf("%d\t",**ppp);就會出現記憶體錯誤。所以在此我們需要加上一個括弧(*ppp)++,這樣*ppp中的才是b[0][0]的地址,接下來通過使用++操作和printf("%d\t",**ppp);才能成功的列印出二維數組b的元素。