打印
[应用相关]

【转】C/C++中多维数组做参数情况的详细解释

[复制链接]
974|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
一灯大神|  楼主 | 2017-1-10 12:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

我大二刚学完C语言,之后用来写矩阵分析的时候想把二维矩阵直接传到函数里,结果出现了问题:形参实参类型不一致,无法通过编译!随后我就尝试各种方法(改变形参或者实参的格式),虽然最后通过了不过当时还是没理解原理。

后来自己把原因分析出来了,现在把它写出来,希望对碰到同样问题的朋友有所帮助。

转载请注明出处,谢谢!

几个跟参数有关的知识:C/C++的函数形参可以是普通类型、指针、引用。传值方式有两种:值传递(包括指针)、引用。  传参时从左往右,结合时从右往左,这个很重要(函数默认值与此有关)。

参数是指针时,我们一般通过两种方式实现读写:①移动指针 p++  ② p+i(目标位置)或者 p,等同于寻址的方式实现,他们实现时在内存里的操作: 一维 p+0(p[0]) p+1(p[1]) p+2(p[2]) ······ p+(n-1)  (p[n-1]) 由于作图不太方便,下面的讲解就不附图了。

1、一维数组(指针)  

做参数 一般指针做参数我就不多说了,专门搜这种问题的人应该都懂。

下面说一下一维数组: 一般传参情况:字符串、整型数组(举个特例,实际上字符串是字符型数组)。 字符串,我们一般用下面这种方式:


[cpp] view plain copy


  • bool PrintStr(char* str)//char str[]也一样  
  • {  
  •     if (NULL == str || "" == str)  
  •     {  
  •         return false;  
  •     }  
  •     for (int i = 0; i < strlen(str);i++)//就不考虑效率了,注意不要用sizeof  
  •     {  
  •         cout << str << "  ";  
  •     }  
  •     while ('\0' != *str)//通过指针  
  •     {  
  •         cout << *str++ << "  ";  
  •     }  
  •     return true;  
  • }  




2、二维数组做参数

在一维中我们看到,遍历数组时必须有终止条件,可以是某种标志也可以规定移动次数。
当到二维的时候这种情况就更为复杂。很多人定义函数的时候将函数定义为双重指针,传参的时候直接把二维数组名放进去,例如:

[cpp] view plain copy


  • //matrix 一维矩阵,m 矩阵行数,n矩阵列数  
  • bool PrintMatrix(int** matrix,int m,int n)  
  • {  
  •     return true;  
  • }  
  •   
  •   
  • int main(void)  
  • {  
  •     int arr[3][3] = {1,2,3,4,5,6,7,8,9};  
  •       
  •     PrintMatrix(arr,3,3);  
  •     return 0;  
  • }  


这时候会提示错误:无法从int[3][3]转为int**,类型不兼容!
当然会不兼容!
函数传参的时候并不是你想的那样,他需要知道传入参数的特征:二维数组,数组每一维占多大。因为在函数里面读写实际是以*(p+i) 或者 p++的形式操作的。
二维数组如果那样操作会怎么样?我从第一行跳到第二行,你会这么写  p+1 或者 p++,可是函数怎么操作?
int**p,p+1每次移动sizeof(int*) ==4个字节,*p+1每次会移动sizeof(int)个字节;int p[3][3],p+1每次移动sizeof(p[0]) ==3*4个字节,*p+1每次会移动sizeof(int)个字节。它们是不能等同看待的,否则很容易出现内存错误,甚至系统崩溃。

所以在定义函数的时候如果是一维就可以省略,因为指针是按照sizeof(指针类型)移动;而在二维的时候则必须告诉函数一维以后的数据布局,这样它才不会读写错误甚至越界,一维部分也可以省略。

从抽象而且直接的角度,你也可以这么理解:一维的在内存中线性排列,读写的时候也是线性操作,所以通过了。当你需要处理的内容是非线性的时候,不仅仅要告诉函数我的内容是非线性的,还要告诉他非线性的特征:二维数组,数组每一维特征(空间大小)

例如:


[cpp] view plain copy


  • #define M 3  
  • #define N 3  
  • //matrix 一维矩阵,m 矩阵行数,n矩阵列数;  
  • //M、N必须是常数,而且N必须和传入的参数一致  
  • bool PrintMatrix(int matrix[M][N], int m, int n)  
  • {  
  •     return true;  
  • }  
  • //matrix 一维矩阵,m 矩阵行数,n矩阵列数  
  • //M甚至可以省略,因为有类型int  
  • bool PrintMatrix(int matrix[][N], int m, int n)  
  • {  
  •     return true;  
  • }  
  •   
  •   
  • //matrix 一维矩阵,m 矩阵行数,n矩阵列数  
  • //当然可以这么写,它本来就是这个样子!  
  • bool PrintMatrix(int (*matrix)[N], int m, int n)  
  • {  
  •     return true;  
  • }  


按照上面的方式完美通过,Perfect!

3、多维数组做参数

多维数组和二维数组类似,只不过需要多加几个说明:

例如:int p[M][N][K] ,p[0][0][0+1] 指针移动移动一个类型长度4byte==sizeof(int),p[0][0+1][0] 指针移动一个维 K*sizeof(int),p[0+1][0][0]指针移动二维N*(K*sizeof(int))。


[cpp] view plain copy


  • #define M 3  
  • #define N 3  
  • #define K 3  
  • bool PrintMatrix(int(*matrix)[N][K], int m, int n,int k)  
  • {  
  •     return true;  
  • }  




4、总结

通过上面分析可以看出多维指针比较复杂,很容易出错。所以实际用的时候一般用其它方法绕过这个问题:数据整理为一维,处理完后再转成原形式;用结构体或类;一维一维的处理等。

PS:今天登陆发现编码出问题,重新编辑了一下


发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

65

主题

112

帖子

2

粉丝