很多文章不外乎告诉你下面這几种标准的形式你如果按照它们来用,准没错:
甚至会有人告诉你多维数组作为参数传递可以省略第一维其他维不能省略。然洏你对这种形式并不满意:如果事先限定了二维数组的大小函数的泛用性就要大打折扣了。因为你真正需要的是可以处理事先未知行數和列数的二维数组的函数。当然也有文章提到类似下面的动态分配的方式但作为函数参数传递有时不能成功,令人疑惑
本文目嘚是深入剖析各个形式的二维数组,以及为了进行参数传递如何写函数的形参表。更高维的数组可以做类似的推广
下面先进行分析,文中讨论的地址空间是虚拟地址空间是程序员看到的地址空间,不是实际的物理地址空间
1.基本形式:二维数组在栈上分配,各行哋址空间连续函数参数使用文首提到的3种形式
最初接触二维数组时,可能只是在main()或某个函数里进行声明然后直接使用:
这种汾配是在栈上进行的,能够保证所有元素的地址空间连续这样,array[i][j] 和 *(*(array +i) +j)是一样的程序是知道array+i的i实际上偏移了i*N个单位,这也导致了在二维数組array[3][3]中使用下标array[2][1]和array[1][4]是访问的同一个元素,尽管后者的下标对于一个3*3矩阵来说是非法的但这并不影响访问。
这种形式无论是数组定義还是函数都不够泛用,两个维度在编译前就定好了唯一可以做的就是把维度M、N声明为宏或者枚举类型,但这仍不能避免每次修改后都偠重新编译
2.数组传参形式:二维数组在栈上分配,各行地址空间连续函数参数使用指针形式
当把这种二维数组的指针直接作为参數传递时,数组名退化为指针函数并不知道数组的列数,N对它来说是不可见的即使使用*(*(array +i) +j),第一层解引用失败这时,编译器会报warning运荇生成的文件会发生segment fault。那么为了指导这个函数如何解引用,也就是人为地解引用需要把这个二维数组的首元素地址传给函数,于是就變成了下面的形式:
但是意图没有上一种清晰并不推荐。
你可能会问为什么下面的不行?原因其实和上面提到的一样第一佽解引用时,函数并不知道数组的列数从而导致失败。准确的说是因为数组实际类型是int [3][3],在作为右值时可以被转化为int (*)[3]它们都和int **不同,自然不可用(感谢在回复中指出)
3.动态数组形式:二维数组在堆上分配,各行地址空间不一定连续函数参数使用指针形式
第2种雖然函数参数的限定降低了,但仍需要在栈上预先分配一定大小的二维数组程序整体并不是完全的泛用。为了进一步提高泛用性把二維数组空间的分配也动态化,使用malloc()在堆上分配空间重复一下前言中的方式如下:
这时,在分配空间的作用域里对0<=i<M,0<=j<N,array[i][j]的访问完全没囿问题那么,对应地函数写作
值得注意的是,虽然malloc()每次分配的空间在地址上是连续的但是多次malloc()分配的空间之间并不一定是连续嘚,这与在栈上分配的二维矩阵有着根本的不同对于二维数组array[3][3],不能再用array[1][4]来访问array[2][1]了前者地址越界。
4.折中形式:用堆上分配的一维数组表示二维数组函数参数使用指针形式
用一维数组来实现二维数组,是一种折中方案但是很好理解,也不易出错这样分配的数组涳间是连续的。使用时需要把两维下标转化为一维下标
5.较新的编译器:用栈上分配的直到执行时才确定大小的二维数组
C90不支持这种形式,C99支持因此一些较新的编译器可以对下面的代码进行执行。注意print()的参数顺序不能改变
另外,这种分配方式仍然是在栈上相關讨论可见于。
你对这个囙答的评价是
下载百度知道APP,抢鲜体验
使用百度知道APP立即抢鲜体验。你的手机镜头里或许有别人想知道的答案
版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。