将2D数组传递给C++函数

Rog*_*win 294 c++ arrays pointers multidimensional-array

我有一个函数,我想作为一个参数,一个可变大小的2D数组.

到目前为止我有这个:

void myFunction(double** myArray){
     myArray[x][y] = 5;
     etc...
}
Run Code Online (Sandbox Code Playgroud)

我在代码中的其他地方声明了一个数组:

double anArray[10][10];
Run Code Online (Sandbox Code Playgroud)

但是,调用myFunction(anArray)给了我一个错误.

当我传入数组时,我不想复制数组.所做的任何更改都myFunction应该改变数据的状态anArray.如果我理解正确,我只想作为参数传入指向2D数组的指针.该函数还需要接受不同大小的数组.例如,[10][10][5][5].我怎样才能做到这一点?

she*_*ngy 391

将2D数组传递给函数有三种方法:

  1. 该参数是2D数组

    int array[10][10];
    void passFunc(int a[][10])
    {
        // ...
    }
    passFunc(array);
    
    Run Code Online (Sandbox Code Playgroud)
  2. 该参数是一个包含指针的数组

    int *array[10];
    for(int i = 0; i < 10; i++)
        array[i] = new int[10];
    void passFunc(int *a[10]) //Array containing pointers
    {
        // ...
    }
    passFunc(array);
    
    Run Code Online (Sandbox Code Playgroud)
  3. 该参数是指向指针的指针

    int **array;
    array = new int *[10];
    for(int i = 0; i <10; i++)
        array[i] = new int[10];
    void passFunc(int **a)
    {
        // ...
    }
    passFunc(array);
    
    Run Code Online (Sandbox Code Playgroud)

  • 对于第一种情况,参数可以声明为`int(*a)[10]`. (12认同)
  • 对于第二种情况,参数可以声明为`int**`. (9认同)
  • @Overflowh你可以用`array [i] [j]`:)得到`array`的元素 (4认同)
  • 案例2和3不是2D数组,因此这个答案具有误导性.[见这个](http://stackoverflow.com/questions/30117161/why-do-i-need-to-use-type-to-point-to-type). (3认同)
  • 我会添加一个 4. 使用 `vector&lt;vector&lt;int&gt;&gt;` (2认同)

leg*_*s2k 164

固定尺寸

1.通过参考

template <size_t rows, size_t cols>
void process_2d_array_template(int (&array)[rows][cols])
{
    std::cout << __func__ << std::endl;
    for (size_t i = 0; i < rows; ++i)
    {
        std::cout << i << ": ";
        for (size_t j = 0; j < cols; ++j)
            std::cout << array[i][j] << '\t';
        std::cout << std::endl;
    }
}
Run Code Online (Sandbox Code Playgroud)

在C++中通过引用传递数组而不丢失维度信息可能是最安全的,因为不必担心调用者传递不正确的维度(编译器标记不匹配时).但是,动态(freestore)数组不可能实现这一点; 它只适用于自动(通常是堆栈生活)数组,即维度应该在编译时知道.

2.通过指针

void process_2d_array_pointer(int (*array)[5][10])
{
    std::cout << __func__ << std::endl;
    for (size_t i = 0; i < 5; ++i)
    {
        std::cout << i << ": ";
        for (size_t j = 0; j < 10; ++j)
            std::cout << (*array)[i][j] << '\t';
        std::cout << std::endl;
    }    
}
Run Code Online (Sandbox Code Playgroud)

前一个方法的C等价物是通过指针传递数组.这不应该与通过数组的衰减指针类型(3)相混淆,这是常见的流行方法,虽然不如此安全但更灵活.与(1)类似,当数组的所有维度都是固定的并且在编译时已知时使用此方法.请注意,在调用函数时,应该传递数组的地址,process_2d_array_pointer(&a)而不是通过衰减传递第一个元素的地址process_2d_array_pointer(a).

可变尺寸

这些都是从C继承但不太安全,编译器无法检查,保证调用者传递所需的维度.该函数仅保留调用者传入的维度.这些比上述更灵活,因为不同长度的阵列总是可以传递给它们.

需要记住的是,没有将数组直接传递给C中的函数[在C++中它们可以作为引用传递(1) ]; (2)传递指向数组的指针而不是数组本身.始终按原样传递数组成为指针复制操作,这可以通过数组衰减到指针的特性来促进.

3.传递(值)指向衰减类型的指针

// int array[][10] is just fancy notation for the same thing
void process_2d_array(int (*array)[10], size_t rows)
{
    std::cout << __func__ << std::endl;
    for (size_t i = 0; i < rows; ++i)
    {
        std::cout << i << ": ";
        for (size_t j = 0; j < 10; ++j)
            std::cout << array[i][j] << '\t';
        std::cout << std::endl;
    }
}
Run Code Online (Sandbox Code Playgroud)

虽然int array[][10]是允许的,但我不推荐它超过上面的语法,因为上面的语法清楚地表明标识符array是指向10个整数数组的单个指针,而这个语法看起来像是一个2D数组但是指向同一个一个由10个整数组成的数组.这里我们知道单行中元素的数量(即列大小,这里是10),但行数是未知的,因此作为参数传递.在这种情况下,由于编译器可以标记何时传递指向第二维不等于10的数组的指针,因此存在一些安全性.第一个维度是变化的部分,可以省略.请参阅此处了解为什么只允许省略第一维的原因.

4.通过指针指向指针

// int *array[10] is just fancy notation for the same thing
void process_pointer_2_pointer(int **array, size_t rows, size_t cols)
{
    std::cout << __func__ << std::endl;
    for (size_t i = 0; i < rows; ++i)
    {
        std::cout << i << ": ";
        for (size_t j = 0; j < cols; ++j)
            std::cout << array[i][j] << '\t';
        std::cout << std::endl;
    }
}
Run Code Online (Sandbox Code Playgroud)

还有一种替代语法与之int *array[10]相同int **array.在这种语法中[10],它会被忽略,因为它会衰减成指针,从而变为int **array.也许这只是调用者的一个提示,传递的数组应该至少有10列,即使那时行数也是必需的.在任何情况下,编译器都不会标记任何长度/大小违规(它只检查传递的类型是否是指向指针的指针),因此需要行和列计数作为参数在这里有意义.

注意: (4)是最安全的选择,因为它几乎没有任何类型检查和最不方便.人们无法合法地将2D数组传递给此函数; C-FAQ谴责通常的做法,int x[5][10]; process_pointer_2_pointer((int**)&x[0][0], 5, 10);因为它可能会因阵列扁平化而导致未定义的行为.在这种方法中传递数组的正确方法将我们带到了不方便的部分,即我们需要一个额外的(代理)指针数组,其每个元素指向实际的,要传递的数组的相应行; 然后将这个代理传递给函数(见下文); 这一切都是为了完成与上述方法相同的工作,这些方法更安全,更清洁,也许更快.

这是一个测试上述功能的驱动程序:

#include <iostream>

// copy above functions here

int main()
{
    int a[5][10] = { { } };
    process_2d_array_template(a);
    process_2d_array_pointer(&a);    // <-- notice the unusual usage of addressof (&) operator on an array
    process_2d_array(a, 5);
    // works since a's first dimension decays into a pointer thereby becoming int (*)[10]

    int *b[5];  // surrogate
    for (size_t i = 0; i < 5; ++i)
    {
        b[i] = a[i];
    }
    // another popular way to define b: here the 2D arrays dims may be non-const, runtime var
    // int **b = new int*[5];
    // for (size_t i = 0; i < 5; ++i) b[i] = new int[10];
    process_pointer_2_pointer(b, 5, 10);
    // process_2d_array(b, 5);
    // doesn't work since b's first dimension decays into a pointer thereby becoming int**
}
Run Code Online (Sandbox Code Playgroud)

  • `array [i] [j]`只是指针算术,即指向`array`的值,它添加`i`并将结果取消引用为`int*`,它将添加`j`和取消引用该位置,读取"int".所以,不,它不需要知道任何维度.但是,这就是重点!编译器信仰程序员的话,如果程序员不正确,则会产生未定义的行为.这就是我提到案例4是最安全的选择的原因. (2认同)

Zra*_*rax 40

对shengy的第一个建议的修改,你可以使用模板使函数接受一个多维数组变量(而不是存储必须被管理和删除的指针数组):

template <size_t size_x, size_t size_y>
void func(double (&arr)[size_x][size_y])
{
    printf("%p\n", &arr);
}

int main()
{
    double a1[10][10];
    double a2[5][5];

    printf("%p\n%p\n\n", &a1, &a2);
    func(a1);
    func(a2);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

print语句用于显示数组是通过引用传递的(通过显示变量的地址)

  • 您应该使用`%p`来打印指针,即使这样,您必须将其强制转换为`void*`,否则`printf()`会调用未定义的行为.此外,在调用函数时不应使用address(`&`)运算符,因为函数需要一个类型为`double(*)[size_y]`的参数,而你当前传递的是`double(*)[10] [10]`和`double(*)[5] [5]`. (2认同)
  • 仅当在编译时知道数组的大小时,此方法才有效。 (2认同)

Ben*_*ley 20

您可以创建这样的函数模板:

template<int R, int C>
void myFunction(double (&myArray)[R][C])
{
    myArray[x][y] = 5;
    etc...
}
Run Code Online (Sandbox Code Playgroud)

然后通过R和C获得两个尺寸大小.将为每个数组大小创建不同的函数,因此如果您的函数很大并且您使用各种不同的数组大小调用它,这可能会很昂贵.你可以使用它作为这样的函数的包装器:

void myFunction(double * arr, int R, int C)
{
    arr[x * C + y] = 5;
    etc...
}
Run Code Online (Sandbox Code Playgroud)

它将数组视为一维,并使用算术来计算索引的偏移量.在这种情况下,您可以像这样定义模板:

template<int C, int R>
void myFunction(double (&myArray)[R][C])
{
    myFunction(*myArray, R, C);
}
Run Code Online (Sandbox Code Playgroud)

  • `size_t`是数组索引比`int`更好的类型. (2认同)

Lem*_*nPi 19

令人惊讶的是,还没有人提到这一点,但你可以简单地模板化任何2D支持[] []语义.

template <typename TwoD>
void myFunction(TwoD& myArray){
     myArray[x][y] = 5;
     etc...
}

// call with
double anArray[10][10];
myFunction(anArray);
Run Code Online (Sandbox Code Playgroud)

它适用于任何2D"类似阵列"的数据结构,例如std::vector<std::vector<T>>,或用户定义的类型,以最大化代码重用.

  • 嗯,这适用于 C++;C 不支持模板。在 C 中执行它需要宏。 (4认同)
  • 这应该是正确的答案。它解决了所有提到的问题和一些这里没有提到的问题。类型安全、数组编译时不兼容、无指针算术、无类型转换、无数据复制。适用于 C 和 C++。 (3认同)
  • 这个答案还不够。它没有解释如何迭代二维数组的元素。 (3认同)
  • @VHS它是一个类型模板,因此它会使用您传入的任何类型(以及编译器推断出的类型)进行实例化。因此您不必显式定义 TwoD。 (2认同)

das*_*ght 12

anArray[10][10]它不是指向指针的指针,它是一个连续的内存块,适合存储100个double类型的值,编译器知道如何解决因为您指定了维度.您需要将其作为数组传递给函数.您可以省略初始尺寸的大小,如下所示:

void f(double p[][10]) {
}
Run Code Online (Sandbox Code Playgroud)

但是,这不允许您传递最后一个维度为十的数组.

C++中最好的解决方案是使用std::vector<std::vector<double> >:它几乎同样高效,而且更加方便.


Mah*_*esh 8

单维数组衰减到指向数组中第一个元素的指针指针.当2D阵列衰减到指向第一行的指针时.所以,函数原型应该是 -

void myFunction(double (*myArray) [10]);
Run Code Online (Sandbox Code Playgroud)

我更喜欢std::vector原始数组.


Sag*_*hah 8

你可以这样做......

#include<iostream>

using namespace std;

//for changing values in 2D array
void myFunc(double *a,int rows,int cols){
    for(int i=0;i<rows;i++){
        for(int j=0;j<cols;j++){
            *(a+ i*rows + j)+=10.0;
        }
    }
}

//for printing 2D array,similar to myFunc
void printArray(double *a,int rows,int cols){
    cout<<"Printing your array...\n";
    for(int i=0;i<rows;i++){
        for(int j=0;j<cols;j++){
            cout<<*(a+ i*rows + j)<<"  ";
        }
    cout<<"\n";
    }
}

int main(){
    //declare and initialize your array
    double a[2][2]={{1.5 , 2.5},{3.5 , 4.5}};

    //the 1st argument is the address of the first row i.e
    //the first 1D array
    //the 2nd argument is the no of rows of your array
    //the 3rd argument is the no of columns of your array
    myFunc(a[0],2,2);

    //same way as myFunc
    printArray(a[0],2,2);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

你的输出如下......

11.5  12.5
13.5  14.5
Run Code Online (Sandbox Code Playgroud)

  • i变量必须乘以列而不是行,除非列和行在这种情况下相等 (2认同)

ras*_*dcs 8

我们可以使用多种方法将二维数组传递给函数:

  • 使用单指针我们必须对二维数组进行类型转换。

     #include<bits/stdc++.h>
     using namespace std;
    
    
     void func(int *arr, int m, int n)
     {
         for (int i=0; i<m; i++)
         {
            for (int j=0; j<n; j++)
            {
               cout<<*((arr+i*n) + j)<<" ";
            }
            cout<<endl;
         }
     }
    
     int main()
     {
         int m = 3, n = 3;
         int arr[m][n] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
         func((int *)arr, m, n);
         return 0;
     }
    
    Run Code Online (Sandbox Code Playgroud)
  • 使用双指针这样,我们也对二维数组进行类型转换

     #include<bits/stdc++.h>
     using namespace std;
    
    void func(int **arr, int row, int col)
    {
       for (int i=0; i<row; i++)
       {
          for(int j=0 ; j<col; j++)
          {
            cout<<arr[i][j]<<" ";
          }
          printf("\n");
       }
    }
    
    int main()
    {
      int row, colum;
      cin>>row>>colum;
      int** arr = new int*[row];
    
      for(int i=0; i<row; i++)
      {
         arr[i] = new int[colum];
      }
    
      for(int i=0; i<row; i++)
      {
          for(int j=0; j<colum; j++)
          {
             cin>>arr[i][j];
          }
      }
      func(arr, row, colum);
    
      return 0;
    }
    
    Run Code Online (Sandbox Code Playgroud)

  • [为什么我不应该#include &lt;bits/stdc++.h&gt;?](/sf/ask/2227126681/) (2认同)

edW*_*edW 8

这是向量矩阵示例的向量

#include <iostream>
#include <vector>
using namespace std;

typedef vector< vector<int> > Matrix;

void print(Matrix& m)
{
   int M=m.size();
   int N=m[0].size();
   for(int i=0; i<M; i++) {
      for(int j=0; j<N; j++)
         cout << m[i][j] << " ";
      cout << endl;
   }
   cout << endl;
}


int main()
{
    Matrix m = { {1,2,3,4},
                 {5,6,7,8},
                 {9,1,2,3} };
    print(m);

    //To initialize a 3 x 4 matrix with 0:
    Matrix n( 3,vector<int>(4,0));
    print(n);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出:

1 2 3 4
5 6 7 8
9 1 2 3

0 0 0 0
0 0 0 0
0 0 0 0
Run Code Online (Sandbox Code Playgroud)