参数传递二维数组
2015-08-21 18:40
232 查看
转自/article/2353876.html
数组名作为形参
编译通过,注意形参声明一定要给出第二个维度的大小,要不编译不过。
一维数组指针作为形参
其实二维数组名就是一个指向一维数组的指针,所以这种声明方式OK。必须指定一维数组的长度,如果没有指定的话,函数声明编译通过。但是如果一旦有调用代码,就有编译不通过,因为没有实参类型能匹配int[].
二维数组引用作为形参
必须指定两个维度的长度。
二维数组指针作为形参
必须指定两个维度的长度。
以上方法都可以等价使用,对数组来说,没有值传递。
还不满足,还有其他的声明方式吗?回到本文开始提到的问题:可以用双重指针int**作为形参,接受二维数组实参吗?答案是肯定的,但是又局限性。用双重指针作为形参,那么相应的实参也要是一个双重指针。事实上,这个双重指针其实指向一个元素是指针的数组,双重指针的声明方式,很适合传递动态创建的二维数组。怎么动态创建一个二维数组?如下程序:
会发现编译不通过,第二个维度长度必须为常量。那么怎么声明一个两个维度都能动态指定的二维数组呢?看下面:
这里为二维数组申请了一段连续的内存,然后给每一个元素指定寻址方式(也可以为每一个元素分别申请内存,就不必指定寻址方式了),最后将双重指针作为实参传递给func5。这里func5多了两个形参,是二维数组的维度,也可以不声明这两个形参,但是为了安全嘛,还是指定的好。最后编译,运行,一切OK。总结一下,上面的代码其实是实现了参数传递动态创建的二维数组。
二、转自:/article/4935237.html
1.基本形式:二维数组在栈上分配,各行地址空间连续,函数参数使用[b]文首提到的3种形式[/b]
最初接触二维数组时,可能只是在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()的参数顺序不能改变。
这段代码出自http://stackoverflow.com/questions/17181577/two-dimensional-arrays-in-c。
(2013.7.28更新)
另外,这种分配方式仍然是在栈上,相关讨论可见于http://bbs.csdn.net/topics/90350681。
小结
其实所谓的二维数组,在K&R上只是指预先分配好大小的形如int a[M][M]这样的数组,它存在于栈上;而实际使用的在堆空间利用malloc动态分配空间的并不是这种,只是用的人多了,把后者叫成二维数组了(我不认为把后者也称为二维数组是标准的说法)。再加上我们经常用它来处理矩阵,“标准的”二维数组、“动态的”“二维数组”、矩阵这三个概念就混在了一起。矩阵是可以用这两种二维数组表示的,而对于这两种不同的二维数组,函数传参的方式也不完全相同,不能随意混用。
C99对于多维数组的描述:
If E is an n -dimensional array ( n ≥ 2) with dimensions i × j × ... × k , then E (used as other than an lvalue) is converted to a pointer to an ( n − 1)-dimensional array with dimensions j × ... × k . If the unary * operator is applied to this pointer explicitly, or implicitly as a result of subscripting, the result is the pointed-to ( n − 1)-dimensional array which itself is converted into a pointer if used as other than an lvalue. It follows from this
that arrays are stored in row-major order (last subscript varies fastest).
栈上分配的二维数组数组名int array[3][3]的真实类型是int [ ][ ],在作为右值时才被转化为(int *array)
(感谢 garbageMan指出),和int **是不同的。把前者进行强制转换为后者,在函数中对元素操作也会导致段错误,下面用图来说明二者区别:
数组名作为形参
void func1(int iArray[][10]) { } int main() { int array[10][10]; func1(array); }
编译通过,注意形参声明一定要给出第二个维度的大小,要不编译不过。
一维数组指针作为形参
void func2(int (*pArray)[10]) { } void func2_1(int (*pArray)[]) //编译通过,无法调用 { } int main() { int array[10][10]; func2(array); }
其实二维数组名就是一个指向一维数组的指针,所以这种声明方式OK。必须指定一维数组的长度,如果没有指定的话,函数声明编译通过。但是如果一旦有调用代码,就有编译不通过,因为没有实参类型能匹配int[].
二维数组引用作为形参
void func3(int (&pArray)[10][10]) { } int main() { int array[10][10]; func3(array); }
必须指定两个维度的长度。
二维数组指针作为形参
void func4(int (*pArray)[10][10]) { } int main() { int array[10][10]; func4(&array); }
必须指定两个维度的长度。
以上方法都可以等价使用,对数组来说,没有值传递。
还不满足,还有其他的声明方式吗?回到本文开始提到的问题:可以用双重指针int**作为形参,接受二维数组实参吗?答案是肯定的,但是又局限性。用双重指针作为形参,那么相应的实参也要是一个双重指针。事实上,这个双重指针其实指向一个元素是指针的数组,双重指针的声明方式,很适合传递动态创建的二维数组。怎么动态创建一个二维数组?如下程序:
int main() { int m = 10; int n = 10; int** p = new int[m] ; }
会发现编译不通过,第二个维度长度必须为常量。那么怎么声明一个两个维度都能动态指定的二维数组呢?看下面:
void func5(int** pArray, int m, int n) { } #include <ctime> int main() { int m = 10; int n = 10; int** pArray = new int* [m]; pArray[0] = new int[m * n]; // 分配连续内存 // 用pArray[1][0]无法寻址,还需指定下标寻址方式 for(int i = 1; i < m; i++) { pArray[i] = pArray[i-1] + n; } func5(pArray, m, n); }
这里为二维数组申请了一段连续的内存,然后给每一个元素指定寻址方式(也可以为每一个元素分别申请内存,就不必指定寻址方式了),最后将双重指针作为实参传递给func5。这里func5多了两个形参,是二维数组的维度,也可以不声明这两个形参,但是为了安全嘛,还是指定的好。最后编译,运行,一切OK。总结一下,上面的代码其实是实现了参数传递动态创建的二维数组。
二、转自:/article/4935237.html
1.基本形式:二维数组在栈上分配,各行地址空间连续,函数参数使用[b]文首提到的3种形式[/b]
最初接触二维数组时,可能只是在main()或某个函数里进行声明,然后直接使用:
... int array[M] ; //array[] ={{...},...,{...}}; is ok //array[][] = {{...},...,{...}}; is wrong ... //使用array[m]
这种分配是在栈上进行的,能够保证所有元素的地址空间连续。这样,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。那么,为了指导这个函数如何解引用,也就是人为地解引用,需要把这个二维数组的首元素地址传给函数,于是就变成了下面的形式:
#include <stdio.h> #include <stdlib.h> #include <assert.h> int func(int *array, int m, int n) { int i,j; for(i=0;i<m;i++) { for(j=0;j<n;j++) printf("\t%d", *(array +i*n +j)); printf("\n"); } return 0; } int main(int argc,char** argv) { int m=3,n=3,i; int array[][3] = {{1,2,3},{4,5,6},{7,8,9}}; func(*array,m,n); return 0; }
你可能会问,为什么下面的不行?原因其实和上面提到的一样,第一次解引用时,函数并不知道数组的列数,从而导致失败。准确的说,是因为数组实际类型是int [3][3],在作为右值时可以被转化为int (*)[3],它们都和int **不同,自然不可用。
int func(int **array, int m, int n) { ... printf("\t%d", *(*array +i*n +j)); ... } int main() { int array[3][3] = { {1,2,3}, {4,5,6}, {7,8,9} }; ... func(array,3,3); ... }
3.动态数组形式:二维数组在堆上分配,各行地址空间不一定连续,函数参数使用指针形式
第2种虽然函数参数的限定降低了,但仍需要在栈上预先分配一定大小的二维数组,程序整体并不是完全的泛用。为了进一步提高泛用性,把二维数组空间的分配也动态化,使用malloc()在堆上分配空间,重复一下前言中的方式如下:
int **array; array = (int **)malloc(m *sizeof(int *)); for(i=0;i<M;i++) array[i] = (int *)malloc(n *sizeof(int));
这时,在分配空间的作用域里,对0<=i<M,0<=j<N,array[i][j]的访问完全没有问题。那么,对应地,函数写作
int func(int **array,int m,int n) { ... printf("%d ", *(*(array+i)+j)); ... }
值得注意的是,虽然malloc()每次分配的空间在地址上是连续的,但是多次malloc()分配的空间之间并不一定是连续的,这与在栈上分配的二维矩阵有着根本的不同,对于二维数组array[3][3],不能再用array[1][4]来访问array[2][1]了,前者地址越界。
4.折中形式:用堆上分配的一维数组表示二维数组,函数参数使用指针形式
用一维数组来实现二维数组,是一种折中方案,但是很好理解,也不易出错。这样分配的数组空间是连续的。使用时需要把两维下标转化为一维下标。
#include <stdio.h> #include <stdlib.h> #include <assert.h> int func(int *array, int m, int n) { int i,j; for(i=0;i<m;i++) { for(j=0;j<n;j++) printf("\t%d",*(array+i*n+j)); printf("\n"); } return 0; } int main(int argc,char** argv) { int m,n,i; int *array; assert(argc == 3); m = atoi(argv[1]); n = atoi(argv[2]); array = (int*)malloc(m*n*sizeof(int)); for(i=0;i<m*n;i++) array[i] = i; func(array,m,n); return 0; }
5.较新的编译器:用栈上分配的直到执行时才确定大小的二维数组
C90不支持这种形式,C99支持,因此一些较新的编译器可以对下面的代码进行执行。注意print()的参数顺序不能改变。
void print(int x, int y, int a[x][y]){ printf("\n"); int i, j; for(i = 0; i < x; i++){ for(j = 0; j < y; j++) printf("%d ", a[i][j]); printf("\n"); } } // Function to initialize the two-dimensional array void init_2d(int *a, int x, int y){ int i, j; for(i = 0; i < x; i++){ for(j = 0; j < y; j++){ a[i*y + j] = i + j; } printf("\n"); } } int main(){ int m , n ; scanf("%d %d",&m,&n); int a[m] ; // a two dimensional whose size has been defined using variables init_2d(a, m, n); print(m, n, a); }
这段代码出自http://stackoverflow.com/questions/17181577/two-dimensional-arrays-in-c。
(2013.7.28更新)
另外,这种分配方式仍然是在栈上,相关讨论可见于http://bbs.csdn.net/topics/90350681。
小结
其实所谓的二维数组,在K&R上只是指预先分配好大小的形如int a[M][M]这样的数组,它存在于栈上;而实际使用的在堆空间利用malloc动态分配空间的并不是这种,只是用的人多了,把后者叫成二维数组了(我不认为把后者也称为二维数组是标准的说法)。再加上我们经常用它来处理矩阵,“标准的”二维数组、“动态的”“二维数组”、矩阵这三个概念就混在了一起。矩阵是可以用这两种二维数组表示的,而对于这两种不同的二维数组,函数传参的方式也不完全相同,不能随意混用。
C99对于多维数组的描述:
If E is an n -dimensional array ( n ≥ 2) with dimensions i × j × ... × k , then E (used as other than an lvalue) is converted to a pointer to an ( n − 1)-dimensional array with dimensions j × ... × k . If the unary * operator is applied to this pointer explicitly, or implicitly as a result of subscripting, the result is the pointed-to ( n − 1)-dimensional array which itself is converted into a pointer if used as other than an lvalue. It follows from this
that arrays are stored in row-major order (last subscript varies fastest).
栈上分配的二维数组数组名int array[3][3]的真实类型是int [ ][ ],在作为右值时才被转化为(int *array)
(感谢 garbageMan指出),和int **是不同的。把前者进行强制转换为后者,在函数中对元素操作也会导致段错误,下面用图来说明二者区别:
相关文章推荐
- linux tomcat 8080访问不了
- HDU 5366 The mook jong
- Chapter10. UINavigationController - iOS Programming- The Big Nerd Ranch Guide 读书笔记
- 【读书笔记】iOS-写代码注意事项
- FindControl什么时候才会使用ObjectFromHWnd函数呢?——VCL很难调试,加一个日志函数,记录时间
- C# in depth (第八章 用智能的编译器来防错)
- MFC内存泄露问题
- DOS format was specified but only a single line feed character was found, not 2
- android解决同一个界面上ScrollView和百度地图(ListView等可滚动控件)滚动冲突问题
- C++实参形参
- 【Flume】【源码分析】深入flume-ng的三大组件——source,channel,sink
- BZOJ 1061 [Noi2008]志愿者招募 线性规划
- SDWebImage源码分析
- 【读书笔记】iOS-写代码注意事项
- 我觉得还是好好写博客
- Android自定义控件<迅雷进度条>
- 用VBS控制鼠标,在Excel2010、2013,64位中
- Android Api Demos登顶之路(四十二)Fragment-->Nesting Tabs
- Dapper/SqlMapper映射对应问题
- hdoj 5311 Hidden String 【KMP + 暴力】