这里先讲一维数组:
int a[5];
int * p=&a; / /把一维数组的第一个元素的地址赋给指针p,int *是声明一个指向int类型的指针,比如数组元素是int类型,那么指针指向的就是元素,也就是int类型(记住: 指针变量存放的是地址,不是值,*p是地址)
----error---*p=a; / /不能单独这样写一行,会报错,但是这里写作int *p=a; 也对,因为a数组名代表的是数组首地址,也就是&a,它也表示数组的第一个元素的地址。
/ /赋予指针地址有两种写法,例如:
int a; int* p = &a;
上面的代码也可以写成:
int a; int* p; p = &a;
p是指向整型的指针,而一维数组的第一个元素的地址赋给了指针p,所以p现在存放了这个地址,所以对p取值得到的就是数组a的第一个元素的地址,也就是数组名a,
*p==a==&a[0](地址),
指针p存放首地址(又称数组名),对指针p取值得到首地址,*p==a(地址)
地址存放数组a第一个元素,对数组名a取值得到数组a的第一个元素,**p==*a==a[0](值),
**p==*a==*&a[0]==a[0](值),所以*&a[0]==a[0](值),所以*&两者可以抵消掉。
那么*(*(p+1))呢?
(p+1)是指针偏移1,是存放了第二个元素的地址,*(p+1)是取出第二个元素的地址,
*(p+1)是第二个元素的地址,是存放了第二个元素,*(*(p+1))是取出第二个元素,
*(*(p+1))==a[1]==*&a[1],就是值
*(p+1)+2,是第二个元素的地址偏移2
*p+2,是第一个元素的地址偏移2
接下来讲一下二维数组:
int a[2][3]={
{1,2,3},
{2,3,4}
};
刚刚讲到二维数组可以当作一维数组,意思是说二维数组的每一行就是二维数组的每个元素,第一行数组就是第一个元素,以此类推。
a是数组名,可在二维数组里我们可以把它当指针看,它指向第1行的一维数组,
a看作指针,
对它取值,得到第一行数组a[0]第一个元素a[0][0]的地址,也就是*a==a[0](地址)
对地址取值,得到值,也就是**a==*a[0]==a[0][0](值)
a+1,是指针,就是指向第2行的一维数组,
*(a+1)==a[1](地址)(数组名)
*(*(a+1))==*a[1]==a[1][0](值)
*(a+1)+2,第2行的一维数组的第一个元素的地址偏移2(a[1]+2),也就是&a[1][2](地址)
*(*(a+1)+2),是取出地址&a[1][2]的值,也就是*(*(a+1)+2)==*&a[1][2]==a[1][2](值)
a看作数组名,我们定义一个数组指针int (*p)[5]=a; ,利用数组名把a[0][0]的地址赋给指针p:
int a[2][3]={{1,2,3},{2,3,4}};
int (*p)[5]=a;
/ /a是第一行数组a[0]的地址(二维数组的第一个元素的地址),不是整个数组a的地址,另外,这里写作 int (*p)[5]=&a[0]; 也对
/ / a[0]是第一行数组a[0]第一个元素a[0][0]的地址(一维数组的第一个元素的地址),a[0]==&a[0][0]
---error---(*p)[5]=a; / /不能这样写,会报错
以下代码段帮助理解:上述地址的值都是相同的,但是意义不一样而已,我们只是利用它的值,所以意义不同也没事,只为了赋予指针&a[0][0],只不过用int (*p)[5]=a; 的形式赋予指针地址罢了。
#includeint main() { int a[3][5] = {{1,2,3,4,5}, {2,3,4,5,6}, {3,4,5,6,7}}; int (*p)[5]=a; printf("%pn%pn%pn%pn%pn%p",a,*a,&a[0],a[0],&a[0][0],&a) ; //a,第一行数组a[0]的地址(二维数组的第一个元素的地址) ==&a[0], 数组名是地址 //*a,数组名是指针?是的,可以这么认为 //a[0],第一行数组a[0]第一个元素a[0][0]的地址(一维数组的第一个元素的地址) ==&a[0][0],数组名是地址 //&a,整个数组a的首地址?对,万物都有地址 }
运行结果:
如图显示,地址的值都相同,我们可以利用,虽然彼此意义可能不同,但我们只要值。
int (*p)[5]=a;也可以写成
int (*p)[5]=&a[0];
int (*p)[5]=*a;
int (*p)[5]=a[0];
int (*p)[5]=&a[0][0];
p指向包含5个元素的一维数组,指向第1行的一维数组,指针p存放了地址(数组名a[0]),
对p取值就是*p==a[0](地址),
**p就是取出*p(地址)所存放的值,也就是**p==**a==*a[0]==*&a[0][0]==a[0][0](值)
那么*(*(p+1))呢?
*(p+1)是取出指针p+1所存放的地址(数组名a[1]),也就是*(p+1)==*(a+1)==a[1](地址),
*(*(p+1))是取出地址*(p+1)所存放的值,*(*(p+1))==**(a+1)==*&a[1][0]==a[1][0](值)
小题目可以结合做一下:
编程实现,将二维数组arr中第1列与最后一列的元素对调,第2列与倒数第2列的元素对调,依此类推,输出原始数组及调换后的数组。要求,定义行指针p使其指向二维数组arr,而对二维数组的所有操作都必须通过p进行。
例如:若数组arr的原始数据是:
1 2 3 4 5
2 3 4 5 6
3 4 5 6 7
则调换后的数组如下:
5 4 3 2 1
6 5 4 3 2
7 6 5 4 3
答案:
#includeint main() { int arr[3][5] = {{1,2,3,4,5}, {2,3,4,5,6}, {3,4,5,6,7}}; int i, j; int (*p)[5]=arr;//第一行数组的地址(二维数组的第一个元素的地址) int temp; for(i = 0; i < 3; i++)//行 { for(j = 0; j < 5; j++)//列 printf("%d ",*(*(p+i)+j));//*(p+i)是第i+1行的地址,*(p+i)+j,第i+1行第j+1列的地址, *(*(p+i)+j)),第i+1行第j+1列的值 printf("n"); } printf("对调结果如下:n"); for(i = 0; i < 3; i++)//行 { for(j = 0; j < 2; j++)//列 { temp = *(*(p+i)+j); //1行1列,1行2列,2行,3行 *(*(p+i)+j) = *(*(p+i)+4-j);//1行5列,1行4列 *(*(p+i)+4-j) = temp;//一行对换后,再下一行对换 } } for(i = 0; i < 3; i++) { for(j = 0; j < 5; j++) printf("%d ",*(*(p+i)+j)); printf("n"); } return 0; }
最后,
总结一下:
指针的作用:赋予指针地址,一般是首地址,然后执行p+1...等操作,使指针偏移,指向某个值的地址,再用 *(取值运算符)对指向的地址取值,得到指向的值,一般用于调用数组的元素,也就是调用值。
那么为什么调用一维数组int a[ ]用int *p=a;就可以将地址&a[0]赋予指针p,而二维数组int a[ ][ ]要用int (*p)[ ]=a;的形式来把地址&a[0][0]赋予指针p呢?
其实很简单啊,
int *p=a,左边是一个指向一个int类型的指针,右边是一个元素的地址(一维数组的第一个元素的地址),一对一;
int (*p)[ ]=a;左边是一个指向存放int类型的数组的指针,数组包含不止一个元素吧,右边是一个数组的地址(二维数组的第一个元素的地址),数组对数组。
再次总结!
以上所有就是用指针调用数组元素!



