前面大致对指针有一定的了解,最近在往后学习 的过程中对指针这玩意儿又有了新的理解。
我认为:指针有2个属性。
一:指针的值(value)
二:指针管辖的宽度(width)
# include <stdio.h>
int main (void)
{
int arr[5]={1,2,3,4,5};
int *p1=arr;
int (*p2)[5]=&arr;
return 0;
}
对指针p1来说,他的值是等于arr的(同样也等于&arr[0]),也就是p1指向数组的第一个元素。这里要强调一下的是,有些人把
数组名这个arr这个指针理解为指向数组的指针,这是错误的,它应该是指向数组的第一个元素的指针。
对p1的宽度来说,它管理arr[0]所占的字节(图中画的是4个字节),也就是说:p1的宽度是sizeof (arr[0])
对于p2,我们从p2的类型可以看出,他是一个指向数组的指针,确切地说是指向有5个int类型元素的数组的指针。
p2的值是&arr ,有经验的coder会在脑子里第一时间想到:&arr 的 值 和 arr 是相等的。的确,因为指针的值就是
它指向的内存块的第一个字节的地址。
对于p2宽度呢?
可以看出:p2是指向数组的指针,它比p1“管得宽”一些。所以,&arr才是指向数组的指针。
这里扩展一下:p2+1 指向什么呢?
好吧,这里,我们的确是访问越界了,不过,还是有必要分析一下。
我们知道,在一个数组中,一个元素的指针+1,代表指向此元素下一个元素的地址,那么在这里,从二位数组的角度
来看,p2+1就是指向与这个数组在内存位置上相邻的下一个同样大小和类型数组的指针。也就是这样(即便这个数组不存在)
验证代码:
# include <stdio.h>
int main (void)
{
int arr[5]={1,2,3,4,5};
int (*p2)[5]=&arr;
printf("%d\n", *((int*)(p2+1)-1) );
return 0;
}
通过上面的理解,我们又可以轻松理解一个我们知道该这样做但却不知道为什么这样做的例子
我们知道:要定义一个指针,就必须要指明指针的类型,也就是这个指针将指向什么类型的元素。
比如:
int* p ; //p是指向int元素的指针
float * p //p指向float型
void (*p) (int ,int ); //p指向一个函数,该函数无返回值,有2个int型参数
好,我们写一个处理二位数组的函数,就要在函数的参数之一定义为一个指向二维数组的“第一个元素”的指针,
我们还了解,第一个元素,他是一个数组。
所以应该这样: void test (int a[ ] [x] ,....)
或者这样 void test (int (*a)[x] ,....)
我想说的 “知道该这样做却不知道为什么这样做”就是我们常常挂在嘴边的:形参的一维可以省略,但二维不能省略。
也就是上面的x不能省略,而且要是一个确定的常量。
为什么呢?到这里应该知道了吧。
上面2中定义方法本质上是第2种,也就是把参数定义为一个指向数组的指针。
既然是定义指针,当然要指明他的类型吶。上面数a是指向什么样的数组的啊?答:指向有x个int型元素的数组的。
可见,x是不可以省略的。
over