指針與數(shù)組


二級指針與二維數(shù)組

char *string[] ={"abc","abcd","acf"};
char string[3][4]={"abc","abcd","acf"};
`
首先一點的是,雖然二維數(shù)組的數(shù)組名可以看做是一個指針,但是并不能將二維數(shù)組的數(shù)組名賦值給一個二級指針,也就是如下的代碼

int main(void)
{
    int arr[3][3] = {{1,2,3},{4,5,6},{7,8,9}};
    int **p = arr;
    return 0;
}

上面的代碼在執(zhí)行的時候會報錯,這是因為兩者數(shù)據(jù)類型是不相同的,所以不能賦值。
二維數(shù)組的數(shù)組名指向的是一維數(shù)組,也就是指向數(shù)組類型,但是二級指針指向的是一級指針,也就是指針類型,所以兩者不能互相賦值。

下面詳細(xì)介紹指針與二維數(shù)組的關(guān)系
聲明一個二維數(shù)組

int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
for (i=0;i<3;i++)
    {   for(j=0;j<4;j++)
        printf("%p ->%d\n",&arr[i][j],arr[i][j]);
    }
000000000062FE10 ->1
000000000062FE14 ->2
000000000062FE18 ->3
000000000062FE1C ->4
000000000062FE20 ->5
000000000062FE24 ->6
000000000062FE28 ->7
000000000062FE2C ->8
000000000062FE30 ->9
000000000062FE34 ->10
000000000062FE38 ->11
000000000062FE3C ->12

二維數(shù)組a可以看成是由a[0],a1,a[2]三個元素組成的一維數(shù)組,a是其數(shù)組名;a[0],a1,a[2]又都是一維數(shù)組,其數(shù)組名分別是a[0],a1,a[2],a[0],a1,a[2]分別表示各自數(shù)組的首個元素的地址。

arr[0]=000000000062FE00 ->1
arr[1]=000000000062FE10 ->5
arr[2]=000000000062FE20 ->9

由于數(shù)組名就是指針,故a[0],a1,a[2]為一級指針,其類型為int。a是由三個一級指針組成的數(shù)組。

那么a與a[0],a+1與a[0]+1的區(qū)別是什么?



a+1跨過了二維數(shù)組的一行,故a,a+1和a+2為行地址。
相對于a[0]而言,a[0]+1只跨過了二維數(shù)組的一個元素,故為列地址。
由此可以看出:
(1)a+i是第i行的地址,即*(a+i)=a[i]。因此a[i][j]等價于(*(a+i))[j];
(2)a[i]+j是第i行第j列的地址,因此*(a[i]+j)等價與a[i][j]。

上述等價關(guān)系總結(jié)如下:

a[i][j] ←→ (*(a+i))[j] ←→ *(a[i]+j) ←→ *(*(a+i)+j)

要想理解指針和二維數(shù)組之間的關(guān)系,就要首先弄清楚各種數(shù)組名所代表的元素。
數(shù)組名表示的是該數(shù)組首個元素的地址,即arr=&arr[0][0];

二維數(shù)組在內(nèi)存中也是以一維數(shù)組的形式存儲,只不過每個的一維數(shù)組的元素都是一維數(shù)組,因此二維數(shù)組名,可以將它看做是指向行數(shù)組的指針
對于二維數(shù)組的存儲,是按照先行后列的形式排列的,把每一行看做是一個一位數(shù)組,那二維數(shù)組就是由多個一位數(shù)組組成的數(shù)組,即二維數(shù)組是數(shù)組的數(shù)組。

對于二維數(shù)組名,可以將它看做是指向行數(shù)組的指針,也就是說二維數(shù)組的元素是行數(shù)組,所以對于二維數(shù)組加減的變化是以行數(shù)組的大小為單位的,即arr指向arr[0]這個行數(shù)組,arr+1指向的是arr1這個行數(shù)組。對其進行解引用,得到的是每一行數(shù)組的首地址,即*arr表示的是第0行數(shù)組的首地址,和arr[0]相等,*(arr+1)表示的是第1行數(shù)組的首地址,和arr1是相等的。假如要取第1行第2個元素的地址,就是arr1+2,因為此時arr1代表的是一維數(shù)組,所以它的元素就是一個數(shù)字,在加減的時候移動的就是元素的大小,例如+1就表示該數(shù)組中第1個元素,+3就表示數(shù)組中的3個元素(以0開始)。因為
*(arr+1)和arr1相等,所以第1行第2個元素的地址也可以表示為*(arr+1)+2,對這個地址進行解引用,得到的就是數(shù)組元素的具體值,也就是((arr+1)+2)。
所以有如下公式,假如一個二維數(shù)組每行有N個元素,二維數(shù)組名是arr,那第i行第j個元素的地址是*(arr+i)+j,也可以表示為arr[i]+j。元素的值可以表示為*(*(arr+i)+j),或者可以表示為arr[i][j]。

對上述二維數(shù)組arr,雖然arr[0]、arr都是數(shù)組首地址,但二者指向的對象不同,arr[0]是一維數(shù)組的名字,它指向的是arr[0]數(shù)組的首元素,其值為arr[0]首個元素的地址,對其進行“*”運算,得到的是一個數(shù)組元素值,即arr[0]數(shù)組首元素值(本例arr[0]數(shù)組的首個元素值為1),因此,*arr[0]與arr[0][0]是同一個值。

而arr(可以看作arr+0)是一個二維數(shù)組的名字,它指向的是它所屬元素的首元素,而二維數(shù)組的每一個元素都是一個維數(shù)組,因此arr指向二維數(shù)組arr的首個元素arr[0],對其進行*運算得到的是arr[0]的地址,而要想得到arr[0][0]的值,需要再進行一次*運算。

*arr=&arr[0];
**arr=*(&arr[0])=arr[0][0]
printf("arr=%p ->%d\n",arr,**arr); //兩次解應(yīng)用得到arr[0][0]
printf("*arr=%p->%d\n",*arr,**arr) ;
printf("arr[0]=%p ->%d\n",arr[0],*(arr[0]));   //arr[0][0]地址

printf("%p\n",&arr[0][0]); 
printf("%p\n",&arr[0]);
printf("%p\n",*(arr+2)+3);                     //&arr[2][3]
printf("%p\n",arr[2]+3);               
printf("%p ->%d\n",*(arr+2)+3,*(*(arr+2)+3));
printf("%p ->%d\n",&arr[2][3],arr[2][3]);
arr=000000000062FE00 ->1
*arr=000000000062FE00->1
arr[0]=000000000062FE00 ->1

000000000062FE00
000000000062FE00
000000000062FE2C
000000000062FE2C
000000000062FE2C ->12
000000000062FE2C ->12

因此,數(shù)組名最為指針時移動單位的是“行”,所以arr+i指向的是第i個行數(shù)組,即指向arr[i]。對arr進行“*”運算,得到的是一維數(shù)組arr[0]的首地址,即\arr與arr[0]是同一個值但指向類型不同通過(arr+1與arr[0]+1即能看出其不同)。當(dāng)用int*p定義指針p時,p的指向是一個int型數(shù)據(jù),對其進行加減操作,移動的是一個int型大小,而不是一個地址,因此,用arr[0]對p賦值是正確的,而用arr對p賦值是錯誤的。
使用數(shù)組指針對二級指針操作
而int (*ptr)[4],ptr表示一個指向4個int型數(shù)組的指針,對其進行加減操作,該變量為數(shù)組大小乘以數(shù)據(jù)類型大小。即ptr+1=&ptr+4*sizeof(int);
因而,int (*ptr)[4]可以使用arr來初始化,因為ptr是一個指向包含4個int型元素的數(shù)據(jù),而arr指向其首個元素的地址,其首個元素又是一個包含4個int型數(shù)據(jù)的數(shù)據(jù)。

int (*ptr)[4]=arr;                              //  行地址
printf("arr=%p->%d\n",&arr[0][0],arr[0][0]);    //首元素地址
printf("ptr=%p->%d\n",ptr,**ptr);     
printf("ptr+1=%p->%d\n",ptr+1,**(ptr+1));       //行地址加一移動4個int型大小
printf("*(ptr+1)+1=%p->%d\n",*(ptr+1)+1,*(*(ptr+1)+1));  //
運行結(jié)果:
arr=000000000062FE00->1
ptr=000000000062FE00->1
ptr+1=000000000062FE10->5
*(ptr+1)+1=000000000062FE14->6

完整程序:

#include<stdio.h>

int main()
{
 int i,j=0;
    int arr[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
    //分別使用不同的形式打印第2行,第3個元素的值和地址
    //根據(jù)推到出來的規(guī)律如下
    //該元素的地址
    int  *p;                  // p+1 移動一個int 類型的大小  ,列地址
    arr+1 ;                   //移動一個int a[4] 類型的大小,行地址
   // p=arr;                    // 錯誤,類型不匹配
    p=*arr;                   // 合法,等同與p=*(a+0)
    p=arr[0];                 // 合法,arr[0]表示數(shù)組arr[0]的首個元素arr[0][0]地址.p=&arr[0][0]
        for(i=0;i<3;i++)
     printf("arr[%d]=%p ->%d\n",i,arr[i],*(arr[i]));
     
    int (*ptr)[4]=arr;
    printf("arr=%p->%d\n",&arr[0][0],arr[0][0]);
    printf("ptr=%p->%d\n",ptr,**ptr);
    printf("ptr+1=%p->%d\n",ptr+1,**(ptr+1));
     printf("*(ptr+1)+1=%p->%d\n",*(ptr+1)+1,*(*(ptr+1)+1));
    printf("arr=%p ->%d\n",arr,**arr); //兩次解應(yīng)用得到arr[0][0]
    printf("*arr=%p->%d\n",*arr,**arr) ;
    printf("arr[0]=%p ->%d\n",arr[0],*(arr[0]));   //arr[0][0]地址
    printf("\n");
    printf("%p\n",&arr[0][0]); 
    printf("%p\n",&arr[0]);
    printf("%p\n",*(arr+2)+3);              //&arr[2][3]
    printf("%p\n",arr[2]+3);               

    
    
    
    
    //該元素的值是12
    printf("%p ->%d\n",*(arr+2)+3,*(*(arr+2)+3));
    printf("%p ->%d\n",&arr[2][3],arr[2][3]);
     printf("\n");
   
    for (i=0;i<3;i++)
    {   for(j=0;j<4;j++)
        printf("%p ->%d\n",&arr[i][j],arr[i][j]);
    }
    return 0;
}

執(zhí)行結(jié)果如下:

ptr=000000000062FE00->1
ptr+1=000000000062FE10->5
*(ptr+1)+1=000000000062FE14->6
arr=000000000062FE00 ->1
*arr=000000000062FE00->1
arr[0]=000000000062FE00 ->1

000000000062FE00
000000000062FE00
000000000062FE2C
000000000062FE2C
000000000062FE2C ->12
000000000062FE2C ->12

000000000062FE00 ->1
000000000062FE04 ->2
000000000062FE08 ->3
000000000062FE0C ->4
000000000062FE10 ->5
000000000062FE14 ->6
000000000062FE18 ->7
000000000062FE1C ->8
000000000062FE20 ->9
000000000062FE24 ->10
000000000062FE28 ->11
000000000062FE2C ->12

指針數(shù)組
char *strs[]={"abc","def","acfg"};
指針數(shù)組是一個元素均為指針的數(shù)組,數(shù)組的數(shù)組名表示數(shù)組首個元素的地址,而首個元素又是指針,因此指針數(shù)組的數(shù)組名就是一個二級指針。
二維數(shù)組名表示的是首個元素的地址,二維數(shù)組又可以理解為一個一維數(shù)組,而每個一維數(shù)組的元素又是
一個一維數(shù)組。對其進行加減操作,是以一維數(shù)組的大小為單位進行。類似與數(shù)組指針,即指向數(shù)組的指針,對其進行加減操作也是以數(shù)組大小為單位進行。

此處輸入圖片的描述
此處輸入圖片的描述

int douptr(char** a,int size)
{
    char **p=a;
    int c=strlen(*p+2);
    printf("a=%p\n",a);
    printf("p=%p\n",p);
    printf("%s\n",*p);
    printf("%c",**p);
    
}
int main()
{
    int i,j=0;
    char *strs[] = {"abc","def","acfg"};  
    
    int size=sizeof(arr)/sizeof(char *);      //數(shù)組大小
    printf("arr=%p\n",arr);                   //數(shù)組地址,數(shù)組名表示首個元素地址
    printf("&arr[0]=%p\n",&arr[0]);           //數(shù)組首個元素地址
    printf("&arr[1]=%p\n",&arr[1]);
    printf("&arr[2]=%p\n",&arr[2]);    
    printf("&arr[0]=%p\n",arr);               //數(shù)組首個元素地址
    printf("arr[0]->%s\n",*arr);              //數(shù)組首個元素指向內(nèi)容
    printf("arr[1][1]=%c\n",*(*(arr+1)+1)) ;  //第二個元素指向數(shù)組的第二個元素
    printf("arr[1][1]=%c\n",arr[1][1]);       //  
    printf("size=%d\n",size);
    douptr(arr,size);

    return 0;
}

結(jié)果:

arr=000000000062FE30
&arr[0]=000000000062FE30
&arr[1]=000000000062FE38
&arr[2]=000000000062FE40
&arr[0]=000000000062FE30
arr[0]->abc
arr[1][1]=e
arr[1][1]=e
size=3
a=000000000062FE30
p=000000000062FE30
abc
a

參考文獻(xiàn)
[1] http://ssspure.blog.51cto.com/8624394/1694224
[2] https://v.qq.com/x/cover/3dwhoro4c9voh54/h14042xdbdp.html
[3] http://ssspure.blog.51cto.com/8624394/1694224
[4] http://c.biancheng.net/cpp/html/478.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,406評論 6 538
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,034評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,413評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,449評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,165評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,559評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,606評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,781評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,327評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,084評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,278評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,849評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,495評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,927評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,172評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,010評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,241評論 2 375

推薦閱讀更多精彩內(nèi)容

  • 指針是C語言中廣泛使用的一種數(shù)據(jù)類型。 運用指針編程是C語言最主要的風(fēng)格之一。利用指針變量可以表示各種數(shù)據(jù)結(jié)構(gòu); ...
    朱森閱讀 3,464評論 3 44
  • 1、一維數(shù)組 定義一個指針P 賦值p=&a[0] a+i=&a[i]=p+i *(a+i)=a[i] 僅當(dāng)p...
    PLW閱讀 1,355評論 0 1
  • C語言中的指針與數(shù)組 @(C語言)[排序算法, 快速排序, C實現(xiàn)] 引言 相信指針與數(shù)組是不少同學(xué)在初學(xué)C語言時...
    harry502閱讀 481評論 0 2
  • !!!注意:因為簡書的顯示格式緣故,所以“ * ”顯示會出現(xiàn)問題,可能有些星號由于疏忽未改動格式,造成沒有顯示,請...
    Eric_Hunter閱讀 516評論 0 1
  • 你是我心中的歌 吟唱出來是心脈的節(jié)奏 我的心曾滿縛枷鎖 你把它釋放出來橫沖直撞 它也曾嘗試橫流四海 但無論如何行走...
    落日丹楓閱讀 182評論 0 1