2D数组作为函数的参数

Car*_*901 7 c++

为什么不像普通数组那样在函数中声明2D数组参数?

 void F(int bar[]){} //Ok
 void Fo(int bar[][]) //Not ok
 void Foo(int bar[][SIZE]) //Ok
Run Code Online (Sandbox Code Playgroud)

为什么需要声明列的大小?

Cod*_*ash 18

静态数组:

你似乎没有完全明白这一点.我想过尝试解释一下.作为一些上述答案描述中,2D ArrayC++被存储在存储器中作为一个1D Array.

int arr[3][4] ;   //consider numbers starting from zero are stored in it
Run Code Online (Sandbox Code Playgroud)

在记忆中看起来有点像这样.

1000    //ignore this for some moments       1011  
^                                             ^
^                                             ^

0   1   2   3   4   5   6   7   8   9   10   11
|------------|  |-----------|   |-------------|
  First Array    Second Array     Third Array

|----------------------------------------------|    
                Larger 2D Array
Run Code Online (Sandbox Code Playgroud)

考虑到这里,Bigger 2D Array存储为连续的内存单元.它由总的12元件,从011.行3和列是4.如果要访问第三个数组,则需要跳过整个第一个和第二个数组.也就是说,您需要跳过等于cols您想要跳过的数组所乘的数字的元素.它出来了cols * 2.

现在,当您指定维度以访问数组的任何单个索引时,您需要事先告诉编译器确切要跳过多少元素.所以你给它准确的数量cols来执行其余的计算.

那么它如何执行计算?让我们说它适用于column major order,也就是说,它需要知道要跳过的列数.当您指定此数组的一个元素为...时

arr[i][j] ;
Run Code Online (Sandbox Code Playgroud)

编译器自动执行此计算.

Base Address + (i * cols + j) ;
Run Code Online (Sandbox Code Playgroud)

让我们尝试一个索引的公式来测试它的准确性.我们想要访问Array 的3rd元素2nd.我们会这样做......

arr[1][2] ;   //access third element of second array
Run Code Online (Sandbox Code Playgroud)

我们把它放在公式中......

  1000 + ( 1 * 4 + 2 )
= 1000 + ( 6 )
= 1006   //destination address
Run Code Online (Sandbox Code Playgroud)

我们到达1006所在的地址6.简而言之,我们需要告诉编译器cols这个计算的数量.所以我们将它作为参数发送到函数中.

如果我们正在努力3D Array,就像这样......

int arr[ROWS][COLS][HEIGHT] ;
Run Code Online (Sandbox Code Playgroud)

我们必须在函数中发送数组的最后两个维度.

void myFunction (int arr[][COLS][HEIGHT]) ;
Run Code Online (Sandbox Code Playgroud)

现在的公式将成为这个..

Base Address + ( (i * cols * height) + (j * height) + k )  ;
Run Code Online (Sandbox Code Playgroud)

像这样访问它...

arr[i][j][k] ;
Run Code Online (Sandbox Code Playgroud)

COLS告诉编译器跳过数字2D Array,并HEIGHT告诉它跳过数字1D Arrays.等等任何方面等等.

动态数组:

当您询问有关因此声明的动态数组时的不同行为.

int ** arr ;
Run Code Online (Sandbox Code Playgroud)

编译器对它们的处理方式不同,因为a的每个索引都Dynamic 2D Array包含一个地址1D Array.它们可能存在于堆上的连续位置,也可能不存在.它们的元素可以通过各自的指针访问.上面我们的静态数组的动态对应物看起来有点像这样.

1000  //2D Pointer
^
^
2000       2001     2002
^          ^        ^
^          ^        ^
0          4        8
1          5        9
2          6        10
3          7        11

1st ptr  2nd ptr   3rd ptr
Run Code Online (Sandbox Code Playgroud)

假设是这种情况.这里2D Pointer的位置或数组1000.它保存2000自身保存内存位置地址的地址.这里指针算术由编译器完成,由此它判断元素的正确位置.

要分配内存2D Pointer,我们这样做..

arr = new int *[3] ;
Run Code Online (Sandbox Code Playgroud)

并为每个索引指针分配内存,这种方式..

for (auto i = 0 ; i < 3 ; ++i)
  arr[i] = new int [4] ;
Run Code Online (Sandbox Code Playgroud)

在结束时,每个ptr2D Array本身是一个数组.要访问你要做的元素......

arr[i][j] ;
Run Code Online (Sandbox Code Playgroud)

编译器这样做......

*( *(arr + i) + j ) ;
   |---------|
     1st step
|------------------|
      2nd step
Run Code Online (Sandbox Code Playgroud)

在第一步中,将2D Array其取消引用到其适当的位置1D Array,在第二步中,1D Array取消引用以获得适当的索引.这就是为什么Dynamic 2D Arrays在没有提及行或列的情况下发送到函数的原因.

注意: 许多细节都被忽略了,描述中有很多东西,特别是内存映射只是为了给你一个想法.


Gri*_*wes 6

你不能写void Foo(int bar[][]),因为bar衰变到一个指针.想象一下以下代码:

void Foo(int bar[][]) // pseudocode
{
    bar++; // compiler can't know by how much increase the pointer
    // as it doesn't know size of *bar
}
Run Code Online (Sandbox Code Playgroud)

因此,编译器必须知道大小*bar,因此必须提供最右侧数组的大小.