最近编程时遇到了一点bug,重新梳理了一下指针数组和二重指针。
或许很多小伙伴在学习时都曾听说过二维数组的数组名相当于一个二重指针,理解很容易,不过从某种程度上来说这根本就是错的,甚至具有很强的误导性。
比如
int a[2][2];
int **p=a;
在编译上是不通过的,因为从本质上来说,他们根本不是同一个类型。二维数组名本质是数组指针而非二重指针。
这个问题的实际运用区别将会体现在以下方面
一,c++动态内存分配机制
c++的new分配数组时,定义类型为数组指针,其指向的是数组首地址并记录每次跨列。类型特征为类型加跨列加地址。
如 int (*a)[2]=new int[2][2];
此时a的类型与二重指针不等同,不可用二重指针。
如果特别需要,必须用二重指针,那么其版本是
int **a=new int*[2]
for(int i=0;i<2;i++)
a[i]=new int[2];
必须要指明的是a的指向是一个指针数组,指针数组将分配在一个连续的空间。
二,作为函数参数时,数组指针有列限制而二重指针没有。
如 void(int (*a)[n], int **b)
那么这时有时为了需要未确定列数二维数组,有的小伙伴会理所当然的觉得可以将数组指针强制转化成二重指针以规避列数限制。而这将造成以下严重后果:
由于二重指针指向指针变量,那么转化后其指向地址与数组指针指向地址相同这没问题。即指向
a[0][0]地址
可如若进行自增等操作,
p++;
其会理所当然的在原来的地址上跨过指针变量所占字节数
并以新地址的内容当地址去进行操作,也就是a[0][1]的内容进行操作,这将与我们预期的a[1]完全不同并可能发生越界等错误!
#include<iostream>
using namespace std;
int main()
{
int a[2][2]={1,2,3,4};
int (*p1)[2]=a;
int **p=(int **)a;
cout <<*(p1+1)<< endl;
cout <<a[0][0]<< endl;
cout <<*(p+1)<< endl;
return 0;
}
首次
p1=&a[0][0]
p=&a[0][0]
变化后
p1=&a[1][0]
p=&a[0][1]
这根本原因便是因为浅显理解二重指针与数组指针造成的错误。因为二重指针自增行为是在指针数组进行,而此时的指针数组应是一个连续的空间。而对于数组指针而言,自增行为是跨过对应列数倍的类型字节数并访问。
从以上区别可以证明,二重指针与数组指针完全不同,其类型存在本质区别,不等价也不可浅显的转化使用。