二級指針與二維數(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ù)組類型贱鄙,但是二級指針指向的是一級指針缤至,也就是指針類型潮罪,所以兩者不能互相賦值。
下面詳細介紹指針與二維數(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); //兩次解應用得到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即能看出其不同)。當用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); //兩次解應用得到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
參考文獻
[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