上一篇介紹了指針和數(shù)組的區(qū)別和聯(lián)系哀蘑,相信很多同學(xué)都已經(jīng)明白了指針和數(shù)組的用法顿苇,那么如果指針和數(shù)組混合起來撩满,你還會(huì)用嗎蜒程?
指針數(shù)組
指針數(shù)組是一個(gè)數(shù)組,它里面存儲(chǔ)的是指針變量伺帘。比如說 int *p[5]
昭躺,數(shù)組里面有五個(gè)元素,里面存儲(chǔ)的是指針伪嫁。由于[]
比*
的優(yōu)先級(jí)要高领炫,故 p 先與[]
結(jié)合,也就是說 p 是一個(gè)數(shù)組张咳。
數(shù)組指針
數(shù)組指針是一個(gè)指針帝洪,它在32位機(jī)器下占四個(gè)字節(jié)大小似舵,64位機(jī)器下占據(jù)八個(gè)字節(jié)。比如int (*p)[5]
碟狞,表示指向擁有5個(gè)元素的數(shù)組啄枕,示例:
int main()
{
int a[3][5] = {0};
int (*p)[5] = a;
}
需要注意的是,數(shù)組指針后面括號(hào)里面的數(shù)字必須與二維數(shù)組的列數(shù)相同才可以族沃。
地址偏移量
地址偏移量的概念不太好理解频祝,為了便于理解,我們引入房間地址
和樓層地址
這兩個(gè)概念脆淹。
在一維數(shù)組中常空,比如int a[5];
a 代表的是一個(gè)房間地址
,那么 a+1 就會(huì)偏移一個(gè)房間地址
的距離(以數(shù)組中的一個(gè)元素所占內(nèi)存字節(jié)為單位進(jìn)行地址偏移)盖溺,也就是偏移到 a[1]
的地方漓糙。
&a
代表的是一個(gè)樓層地址
,&a+1
會(huì)偏移整個(gè)數(shù)組的長度(以整個(gè)數(shù)組所占內(nèi)存字節(jié)為單位進(jìn)行偏移)烘嘱,也就是偏移到了整個(gè)數(shù)組后面的地方昆禽。
指針數(shù)組操作二維數(shù)組
int main()
{
//指針數(shù)組操作二維數(shù)組
int a[4][5]; //定義一個(gè)二維數(shù)組
int i, j;
int *p[4]; //定義一個(gè)指針數(shù)組(是個(gè)數(shù)組),和二維數(shù)組的行數(shù)相同。
//初始化二維數(shù)組
for(i = 0; i < 4; i++)
{
for(j = 0; j < 5; j++)
{
a[i][j] = 10 * i + j;
}
}
//輸出二維數(shù)組內(nèi)容
for(i = 0; i < 4; i++)
{
for(j = 0; j < 5; j++)
{
printf("a[%d][%d]= %d\t", i, j, a[i][j]);
}
}
printf("\n\n");
p[0] = *a; //指針數(shù)組(數(shù)組),存儲(chǔ)的是"房間"的地址,而不是"樓層"的地址,如果這樣寫p[0] = a;這樣在c++中編譯不通過的
p[1] = *(a + 1); //給第二個(gè)元素賦值
p[2] = *(a + 2); //給第三個(gè)元素賦值
p[3] = *(a + 3); //給第四個(gè)元素賦值
//第一種方式輸出二維數(shù)組內(nèi)容
for(i = 0; i < 4; i++)
{
for(j = 0; j < 5; j++)
{
printf("p[%d][%d]= %d\t", i, j, p[i][j]);
}
}
printf("\n\n");
//第二種方式輸出二維數(shù)組內(nèi)容
for(i = 0; i < 4; i++)
{
for(j = 0; j < 5; j++)
{
printf("*(*(p+%d)+%d)= %d\t",i ,j, *(*(p + i) + j));
}
}
printf("\n\n");
//第三種方式輸出二維數(shù)組內(nèi)容
for(i = 0; i < 4; i++)
{
for(j = 0; j < 5; j++)
{
printf("*(p[%d]+%d)= %d\t", i, j, *(p[i] + j));
}
}
return 0;
}
輸出結(jié)果:
解釋:
p 數(shù)組里面存放的是一級(jí)指針蝇庭,p[0] 保存的是二維數(shù)組第一層樓
的第一個(gè)房間地址
醉鳖,p[1] 保存的是二維數(shù)組第二層樓的第一個(gè)房間地址,p[2] 保存的是第三層樓的第一個(gè)房間地址哮内,p[3] 保存的是二維數(shù)組第四層樓的第一個(gè)房間地址盗棵;之后,p[0][0] 表示:(p[0])[0] == (p 數(shù)組的第一個(gè)元素)[0] == (*a)[0] == (a[0])[0] == a[0][0]北发,其他的以此類推纹因,得出結(jié)果。
數(shù)組指針操作二維數(shù)組
int main()
{
//數(shù)組指針操作二維數(shù)組
int a[4][5]; //定義一個(gè)二維數(shù)組
int i, j;
int (*p)[5]; //定義一個(gè)數(shù)組指針(是個(gè)指針)琳拨,和二維數(shù)組的行數(shù)相同
//初始化二維數(shù)組
for(i = 0; i < 4; i++)
{
for(j = 0; j < 5; j++)
{
a[i][j] = 10 * i + j;
}
}
//輸出二維數(shù)組內(nèi)容
for(i = 0; i < 4; i++)
{
for(j = 0; j < 5; j++)
{
printf("a[%d][%d]= %d\t", i, j, a[i][j]);
}
}
printf("\n\n");
p = a; //讓指針指向二維數(shù)組
// p = *a; //雖然a和*a的值都是一樣的瞭恰,但是這樣無法賦值(a是"樓層地址",*a是"房間地址")
//第一種方式輸出二維數(shù)組內(nèi)容
for(i = 0; i < 4; i++)
{
for(j = 0; j < 5; j++)
{
printf("p[%d][%d]= %d\t", i, j, p[i][j]);
}
}
printf("\n\n");
//第二種方式輸出二維數(shù)組內(nèi)容
for(i = 0; i < 4; i++)
{
for(j = 0; j < 5; j++)
{
printf("*(*(p+%d)+%d)= %d\t",i, j, *(*(p + i) + j));
}
}
printf("\n\n");
//第三種方式輸出二維數(shù)組內(nèi)容
for(i = 0; i < 4; i++)
{
for(j = 0; j < 5; j++)
{
printf("*(p[%d]+%d)= %d\t",i, j, *(p[i] + j));
}
}
return 0;
}
輸出結(jié)果:
解釋:
p 里面存放的是一維數(shù)組的地址(注意不是數(shù)組首元素的地址,是整個(gè)數(shù)組的地址)狱庇,在程序中 a 表示的就是第一層樓的地址寄疏,也就是 &a[0] 。由于 p 指向的是樓層地址僵井,則 p+1 則表示第二層樓的地址陕截,p+2 表示第三層樓的地址...當(dāng)我們用 p[0][0] 的時(shí)候,其實(shí)表示的就是第一層樓的第一個(gè)房間內(nèi)容(p 表示的第一層樓的地址批什,p[0] 表示的第一層樓的第一個(gè)房間的地址农曲,p[0][0] 表示的第一層樓的第一個(gè)房間的內(nèi)容),其他的以此類推。
總結(jié)
- 指針數(shù)組是數(shù)組乳规,數(shù)組指針是指針形葬;它們符合各自數(shù)組或指針的特性;
- 數(shù)組和指針都是操作地址的運(yùn)算暮的;
- 地址也是有級(jí)別的笙以,例如上面所說的
樓層地址
和房間地址
(本質(zhì)是地址操作時(shí)候的偏移單位不同); - 很重要的一點(diǎn)冻辩,數(shù)組的地址是連續(xù)的2蟆!上面的操作都是基于這點(diǎn)才可以實(shí)現(xiàn)的恨闪,如果換成鏈表倘感,則上面程序的輸出結(jié)果將無法判斷;
- 上面提到的指針數(shù)組和數(shù)組指針都屬于
二級(jí)指針
范疇咙咽,如果是int ****p[5]
老玛,你還能操作二維數(shù)組嗎?換成三維數(shù)組呢钧敞?抓住本質(zhì)蜡豹,無論多少級(jí)都一樣操作。
補(bǔ)充
與指針數(shù)組和數(shù)組指針類似的還有一個(gè):二級(jí)指針
溉苛,示例:
int main()
{
int a[4][5];
int i, j;
int *q[4];
int **p;
for(i = 0; i < 4; i++)
{
for(j = 0; j < 5; j++)
{
a[i][j] = 10 * i + j;
}
}
for(i = 0; i < 4; i++)
{
for(j = 0; j < 5; j++)
{
printf("a[%d][%d]= %d\t", i, j, a[i][j]);
}
}
printf("\n\n");
q[0] = *a;
q[1] = *(a + 1);
q[2] = *(a + 2);
q[3] = *(a + 3);
p = q;
for(i = 0; i < 4; i++)
{
for(j = 0; j < 5; j++)
{
printf("p[%d][%d]= %d\t", i, j, p[i][j]);
}
}
printf("\n\n");
for(i = 0; i < 4; i++)
{
for(j = 0; j < 5; j++)
{
printf("*(*(p+%d)+%d)= %d\t",i ,j, *(*(p + i) + j));
}
}
printf("\n\n");
for(i = 0; i < 4; i++)
{
for(j = 0; j < 5; j++)
{
printf("*(p[%d]+%d)= %d\t", i, j, *(p[i] + j));
}
}
return 0;
}
自己輸出一下結(jié)果余素,嘗試?yán)斫庖幌轮羔樀墓ぷ髟恚嘈拍憧梢哉鞣炊昆。。?/p>
練習(xí)
int a[] = {0, 2,4,6,8};
int *p[5]={a, a+1, a+2, a+3, a+4};
int ** pp = p;
int main()
{
printf("%d\n",*pp++);
printf("%d\n", *pp-a);
printf("%d\n", ** ++ pp);
printf("%d\n", pp - p);
return 0;
}
輸出結(jié)果是什么呢威根?
#include <stdio.h>
int main()
{
int a[5] = {1, 2, 3, 4, 5};
int *ptr = (int *)(&a + 1);
printf("%d %d\n", *(a+1), *(ptr-1));
return 0;
}
輸出結(jié)果是什么呢凤巨?