多维数组与指针
多维数组的数组名并不是很多网友描述的多级指针,我仅以二维数组作为研究对象,进行一定的分析。
二维数组int A[M][N],可以认为是存在M个元素的数组,且每一个元素都是长度为N的int型数组,这样就能比较清晰的理解了数组。数组名在很多情况下转换为指针,且数组名是数组首个元素的指针,这是非常重要的概念,搞清楚了这个概念也就能够分析其他多维的数组情况啦。
对于上面的数组A[M][N],数组名A实质上指向数组的首个元素的指针,也就是说这时的数组名A变质为一个指针,指向的类型为int[N],即指向的是一个数组,而不是一个简单的值,那么对数组名进行解引用*A,就是得到这个一维数组(这是指针解引用的定义,就如同 int *p = &a, *p 实质上是指a是一个道理),相当于得到了一维数组名,即*A实质上是一个数组,(*A)可以看做这个数组的数组名,这和int a[N]是同样的道理,即:a = *A。同样根据数组名是指向数组的首个元素的指针,可以将*A也可以被看做为指针,但此时的指针指向的是数组A[0]的首个元素,也就是指向了A[0][0],也就是说*(*A)就是A[0][0]。由此可知,二维数组并不是简单的指向指针的指针,而是每一次解引用对应的都是不同的指针类型。
对于多维的数组,假设存在int B[N][M][S]这个三维的数组,那么数组名B是一个指针,其指向的类型是int[M][S]。*B也是指针,但是其指向的类型是int[S]。**B同样也是指针,但是指向的类型是int型。更多维的数组也可以采用类似方法的分析。所以说并不能说是多维数组的数组名多级指针,因为多维数组对数组名的解引用操作都得到了一个不同大小的数组空间的指针,并不是指向单一元素的指针。
由上面的分析可知,二维数组名A是一个指向一维数组的指针,指针的加法是指在当前地址上加上类型的长度,得到指向的新地址,这里的类型就是数组int[N]。因此A+i实质上是指向二维数组的第i个元素,也就是第i个一维数组。对A+i解引用*(A+i)即得到一个数组A[i],*(A+i)就是这个数组的数组名(这样说可能不合适,但是方便理解),同时依据数组名就是指向这个数组的首个元素的地址,可知*(A+i)实质上还是一个指针,但是这个指针指向的是A[i][0]这个元素。
下面总结一下二维数组中的一些结论:
a :指向一维数组的指针。a -> a[0]
*a :得到一维数组a[0],可认为(*a)是一维数组的数组名,而(*a)-> a[0][0]。
*a+j :指向a[0][j]的指针,(*a)可视为一个一维数组名。
a+i :数组a[i]的指针,也就是说a+i->a[i]。
*(a+i): 得到一维数组a[i],其中*(a+i)可以视为数组名。*(a+i)指向了a[i]的首个元素,即:*(a+i)->a[i][0]。
*(a+i)+j :得到数组*(a+i)的第j个元素的指针,即:*(a+i)+j -> a[i][j]。
*(*(a+i)+j) :就是得到a[i][j]。
我之前一直将二维数组看做指向指针的指针,现在想起来是不合适的,假设存在一个指向指针的指针int **p。如果另p = A,通常对p的操作都会导致一些错误,由于p是一个指向指针的指针,那么p++实质上只是增加了4个字节,而A+1则增加了N*sizeof(int)。两者之间并不是等价的关系,因此指向指针的指针并不能表征二维数组。
如果指向指针的指针a和二维数组名是等价的,那么下面的程序肯定能够实现数组元素的打印,但是非常不幸,下面的函数会产生段错误即访问了非法的内存空间。
int print_array(int **a, int row, int col)
{
int i = 0 , j = 0;
for(i = 0; i < row; ++ i)
{
for(j = 0; j < col; ++ j)
printf("%d ",a[i][j]);
printf("");
}
}
简要的说明一下,为什么会产生段错误吧,由于a是一个指向指针的指针,那么a[i]则是一个指针,他指向一个数据空间,由于数组名实际上对应一个地址,这个地址和&A等都是相同的,a[i]实质上是就是数组中的一个元素,但是其中的内容也是一个指针,这时候再次解引用就很有可能导致内存非法访问,产生段错误。比如A[2][3]={{1,2,3},{4,5,6}}。调用函数print_array(A,2,3)时,由于A实质上是一个地址,由于数组的特殊性,具有&A,A,A+0,&A[0][0]等对应的值是相同的。a = A,实质上a就是一个地址。这个地址就是数组的起始位置。a中的内容实质上就是数组的元素,因此对数组进行一次解引用(*a)得到的实质上就是数组的一个元素,也就是1,但是a
多维数组指针C语 相关文章:
- Windows CE 进程、线程和内存管理(11-09)
- RedHatLinux新手入门教程(5)(11-12)
- uClinux介绍(11-09)
- openwebmailV1.60安装教学(11-12)
- Linux嵌入式系统开发平台选型探讨(11-09)
- Windows CE 进程、线程和内存管理(二)(11-09)