在做Leetcode第36题:有效的数独中,遇到这样一个问题:题目给出这样一个函数 bool isValidSudoku(char** board, int boardRowSize, int boardColSize),很显然三个形参分别是二维数组的数组名、行数和列数。在LeetCode中比较坑的是,不能进行调试,所以我要自己写主函数及输入,调用该函数来进行调试。问题就出在调用该函数这块,不知道如何对二维数组二级指针形式进行传参,看了很多关于此的博客以及书籍,下面在此进行总结下。
#include
#include
void func(int str[2][2],int row,int col) //func(int str[][2],int row,int col)
{
printf("%d",str[1][1]);
//printf("%d",*(*(str + 1)+1)); //实现效果和上一条等价
str[0][0] = -1;
}
int main()
{
int a[2][2] = {1,2,3,4};
func(a,2,2);
printf(" %d",a[0][0]);
getchar();
return 0;
}
这都是最标准的形式,一般正常都会马上想到这种形式。int a[][]这种分配是在栈上进行的,能够保证所有元素的地址空间连续。这样,array[i][j] 和 *(*(array +i) +j)是一样的,程序是知道array+i的i实际上偏移了i*N个单位。
void func(int (*str)[2],int row,int col)
{
printf("%d",str[1][1]);
//printf("%d",*(*(str + 1)+1)); //和上一条代码等价
str[0][0] = -1;
}
int main()
{
int a[2][2] = {1,2,3,4};
func(a,2,2);
printf(" %d",a[0][0]);
getchar();
return 0;
}
二维数组的实质是:以数组为元素的数组。a是一个有着2个数组类型元素的数组,它的每一个数组类型元素又是一个有着2个整型元素的数组。二维数组名a,等价于指向数组a[0]的指针,实际类型是int [2][2],在作为右值时可以被转化为int (*)[2]。int (*str)[2]这个语句的效果是,声明了 *str是一个拥有2个整型元素的数组,因此str就是一个指向这样数组的指针。str+i 程序知道实际上是偏移了i*col个单位,所以str[i][j] 和 *(*(str +i) +j)等价。
但上面3种形式,无论是数组定义还是函数都不够泛用,两个维度在编译前就定好了,唯一可以做的就是把维度M、N声明为宏或者枚举类型,但这仍不能避免每次修改后都要重新编译。
void func(int *str,int row,int col)
{
printf("%d",*(str + 1*2 +1)); //人为解引用,输出str[1][1]
*(str + 0*2 +0) = -1;
}
int main()
{
int a[2][2] = {1,2,3,4};
func(*a,2,2); //二维数组名a,等价于指向数组a[0]的指针。*a等于数组a[0],数组名a[0]代表该数组的首元素地址
printf(" %d",a[0][0]);
getchar();
return 0;
}
虽然我们以表格形式显示二维数组,但实际上它们在计算机的内存中是按行主序连续存储的。这也导致了在二维数组a[2][2]中,使用下标array[1][0]和array[0][2]是访问的同一个元素,尽管后者的下标对于一个2*2矩阵来说是非法的,但这并不影响访问。调用这个函数时,需要把这个二维数组的首元素地址传给函数,并人为指导这个函数如何解引用,也就是把二维数组当做一维数组使。
void func(int **str,int row,int col)
{
printf("%d",str[1][1]); //输出str[1][1]元素
//printf("%d",*(*(str + 1)+1)); //和上一条代码等价
str[0][0] = -1;
}
int main()
{
int i;
int **a;
a = (int **)malloc(2 *sizeof(int *));
for(i=0;i<2;i++)
a[i] = (int *)malloc(2 *sizeof(int));
a[0][0] = 1;
a[0][1] = 2;
a[1][1] = 3;
a[1][1] = 4;
func(a,2,2);
printf(" %d",a[0][0]);
getchar();
return 0;
}
上面一种虽然函数参数的限定降低了,但仍需要在栈上预先分配一定大小的二维数组,程序整体并不是完全的泛用。而我们在现实当中,经常要处理事先未知行数和列数的二维数组的函数 ,这也是为什么LeetCode要用二级指针的形式。 为了进一步提高泛用性,把二维数组空间的分配也动态化,使用malloc()在堆上分配空间。
我只是站在巨人的肩膀上,将我所看到写成这篇博客,有想更深入了解的,可以看看下面的资料。 c语言与陷阱 [美]Andrew koening 著 高巍 译 二维数组作为函数参数传递剖析:https://www.cnblogs.com/wuyuegb2312/archive/2013/06/14/3135277.html