指针数组,故名思义,就是指针的数组,数组的元素是指针;
数组指针,同样,就是指向数组的指针。
首先我们来认识一下多维数组的实质:
Part1:
C语言中所谓的二维数组其实就是一维数组,因为一维数组的每个元素都是一维数组,(把二维数组的每一行当作一个元素,那么纵向来看就是一个一维数组)那么这个一维数组就是所谓的二维数组。
C语言并没有多维数组,我们看到的型如a[2][3][4]是一维数组嵌套而成的。
下面的例子会证明这点:(vc++6.0验证)
int main()
{
char a[2][3] = {{1,2,3},{4,5,6}};
char *p = (char *)a;
int i = 0;
for(;i<6;++i)
{
printf("%d ",p[i]);
printf("%p" ,&p[i]);
printf("\n");
}
return 0;
}
强制转换获取到二维数组第一个元素的地址,然后就完全按照一维数组方式来访问,从地址明显看出来,地址都是连续的,即整个数组的内存都是连续的,与一维数组的情形完全一样。结果完全正确。
小总结:由于内存的线性性(地址线性增加)决定了不可能有多维数组,所有多维数组都是
虚构出来的。
虽然多维数组的实质是一维数组的嵌套而成的,但从使用者的角度来看把它看成多维数组完全没有问题(其实上面的你不看下面的完全可以看懂,上面只是为了拓展各位的知识)。
Part2:
(以下都当做有多维数组来解释,毕竟C语言创造多维数组的用途就是告诉我们可以放心的当使用真的多维数组来看待多维数组吧。)
再来一个预备知识,这点是我的朋友梁锦超点醒我的:
“数组名指向的第一个元素是哪个? ”
答:对于一维数组a[5],第一个元素理所当然的就是a[0]啦,即a指向了a[0],这天下人都不会错。对于二维数组呢a[5][5]呢?是不是第一个元素就是a[0][0]呢?错了,我上面说过,把一行当成一个元素,每一个元素又是一维数组,所以第一个元素就是a[0],也就是第一行。(这个需要你的脑袋去想象,二维也许画得出来,三维可就不可能画出来了)。同理a[5][5][5]的第一个元素也是a[0].
结论,数组名不管几维都是指向第一个元素a[0].
好了,终于到了今天的主题了。
有一种指针类型形如char (*)[n](n是已知常量),定义一个此类型变量的语法为char (*a)[10](注意与char *a[10]相区别,这是个类型为char *的一维数组,两者完全不同)。
char a[10]; //a指向第一个元素a[0](char类型),即a=&a[0]
char *Pa = a; //Pa是指向char类型的指针,所以两者匹配
char b[2][10]; //b是向第一个元素b[0](char[10]类型),即b=&b[0][10]
char (*Pb)[10] = b; //Pb也指向一维数组b[10],所以两者匹配
char c[2][3][10];//c是向第一个元素b[0](char[3][10]类型),即b=&b[0][3][10]
char (*Pc)[2][10] = c;//Pc也指向二维数组c[3][10],所以两者匹配
要把上面这几个例子彻底的弄进脑子里确实是有点困难,我就花了两个星期来推敲,多想,别指望看一遍都觉得懂了,把它彻底印进脑袋里才是你的知识,不然,还是我的。
那么怎么在使用时很好的区分并避免出错呢?
简单举例说明:
int *p[2]; 首先声明了一个数组,数组的元素是int型的指针。
int (*p)[2]; 声明了一个指针, 指向了一个有两个int元素的数组。
其实这两种写法主要是因为运算符的优先级, 因为[]的优先级比*高。
所以第一种写法,p先和[]结合,所以是一个数组,后因有一个()所以p先与*结合,是指针。
指针数组如下处理就会很清楚:
typedef int* intPtr; // intPtr是int* 类型指针
intPtr p[2]; //很清楚开出来是指针数组
一目了然,所以为了避免迷惑,做适当的typedef也是很有必要的。
同理,数组指针也可以作类似处理:
typedef int intArray2[2]; //intArray2是一个有两个元素的一维数组
intArray2 * p;//很明显p是一个指针,指向有两个元素的一维数组
和原来的声明都是等价的。
个人建议编程过程中采用typedef来进行类型定义,
这样程序看起来会清晰很多。
小总结:多利用typedef有利于代码优化。
最后再讨论一种很常见的对多维数组的错误理解,有些人常常会以为,二维数组就是二级指针,这种错误的根源,来自于可以把一个二级指针int **p以 p[i][j]这种形式使用。首先把数组称为指针就是错误的,数组名是地址,不能理解为指针。第二,并非能以p[i][j]这种形式使用,那么p就是一个二维数组了,C标准对数组引用的规定,并没有指定数组引用时[]运算符的左边必须是数组名,而可以是一个表达式。第三,这是一种“巧合”,归根到底是由于C语言的数组实现是数组的嵌套同时C标准把[]运算符转换为类似
*(*(a+i)+j) //如果a是一个**指针,a[i][j]等价于
//*(*(a+i)+j) ,系统自动跨行跳跃到需要的位置
这样的等价表达式造成的,那两个取值运算符“恰好”可以用于一个二级指针。第四,p与p[i]并不具有数组类型,sizeof(p)和sizeof(p[i])的结果只是一个指针的大小4字节。而对于一个真正的数组,p与p[i]都是具有数组类型的地址。
实际上,int **p只是一个指向一维指针数组的指针,而不是指向二维数组的指针。
char * c[3] ;
char ** pc = c;
同样地,对于n级指针,都可以看作一个指向一维指针数组的指针,这个指针数组的元素都是n-1级指针。
char ** d[3];
char *** pd = d ;