二维数组和二级指针
对于如下程序
void fun(int **data,int row,int column)
{
for(int i = 0;i < row; ++ i)
{
for(int j = 0;j < column;++ j)
cout << data[i][j] << "\t";
cout << endl;
}
}
int main()
{
int data[2][3] = {{3,8,4},{4,5,6}};
fun(data,2,3);
return 0;
}
编译无法通过,提示“no known conversion from 'float [2][3]' to 'float **' for 1st argument”
如果在main函数中将data强制转换成二级指针,编译能通过,运行时发生段错误。
究其根本,是因为二维数组名,并不是一个二级指针。
二维数组
二维数组内存分配
一维数组名,数组首元素的地址,是一个指针。
二维数组是”数组的数组“,因而我们很容易产生二维数组名是一个二级指针的错觉,实际上并不是这样的。

如上图所示,实际上,不管是一维还是多维数组,都是内存中一块线性连续空间,因此在内存级别上,其实都只是一维。但是不同的定义使得表现形式不一样,从而有多维数组的概念。
访问数组元素其实非常简单,原因就在于元素在内存中的线性排列,这样对一维数组的访问只需要arr1[index] = *(arr1+index*sizeof(T))
;对二维数组的访问
arr2[i][j]=*(arr2+(i*col+j)*sizeof(T))
,因此连续线性的数组访问效率很高。多维类似。
二维数组与数组指针
对于二维数组int data[2][3],可看做特殊的一维数组data[2],包含两个元素,每个元素的类型是int [3]。
无论如何,数组名总是代表数组首元素的地址,因此,二维数组名是一个数组指针,而非二级指针
那么到底什么是数组指针呢?
数组指针是一个指针,指向一个数组,数组指针的定义有如下形式
int data[2][3]
int (*ptr)[3]
ptr = data;//ptr指向data的第0行
ptr = &data[1];//ptr指向data的第1行
数组指针的声明中,方括号中的维数指明了对应的指针变量偏移一个单位对应的内存偏移量。(如ptr + 1,ptr会向高地址移动3*4Byte)
上述声明中,圆括号不能省略,因为[]具有更高的有衔接,int *var[3]
声明的是一个元素为int *的数组(指针数组)。
实际上,可用typedef 使得定义更简单易懂
typedef int int_array[3];
int_array *ptr = data;
因此,文章开头的程序中fun函数的定义改成如下形式才能够将二维数组data做为实参传入
void fun(int (*data)[3],int row,int column)
或void fun(int data[][3],int row,int column)
为什么不能将二维数组名强制转换成二级指针
void fun(int **data,int row,int column)
{
for(int i = 0;i < row; ++ i)
{
for(int j = 0;j < column;++ j)
cout << data[i][j] << "\t";
cout << endl;
}
}
int main()
{
int data[2][3] = {{3,8,4},{4,5,6}};
int** ptr = (int **)data;
printf("%p,%p\n",ptr,data);
printf("%p\n",ptr[0]);
return 0;
}
运行输出如下结果

ptr和data本质上都是指针,在赋值后,他们指向了同一片内存空间,如下图所示(每个数字为16进制中的一位,包含4bit,所使用测试机器为64位机器)

64位机器中,int占4个字节,int *占8个字节,因此ptr[0]就是data[0][0]和data[0][1]拼接起来的结果,故ptr[0]的值为0x800000003。可以看到,这个值并非data数组首元素的地址。因此当进行如下调用fun(ptr,2,3)时,试图访问的是0x800000003的未知空间,因此发生段错误。
二级指针和指针数组
对于如下程序
int *ptr_arr[2];
for(int i = 0;i < 2;++ i)
{
ptr_arr[i] = new int [3];
for(int j = 0;j < 3;++ j)
ptr_arr[i][j] = i + j;
}
fun(ptr_arr,2,3);
ptr_arr是一个元素为int *变量的数组(注意和数组指针声明的区别),是一个指针数组。
无论如何,数组名总是代表数组中首元素的地址。因此ptr_arr实际上是&ptr_arr[0]
,ptr_arr[0]是一个int *
变量,因此,ptr_arr是指针的指针,也就是二级指针,这也是能够在fun函数中传入ptr_arr的原因。
也就是说,指针数组的数组名等价于一个二级指针