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

Passing a 2D array to a C++ function

我有一个函数,作为参数,我想取一个可变大小的二维数组。

到目前为止,我有:

1
2
3
4
void myFunction(double** myArray){
     myArray[x][y] = 5;
     etc...
}

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

1
double anArray[10][10];

但是,打电话给myFunction(anArray)会给我一个错误。

我不想在传入数组时复制它。对myFunction所做的任何更改都应改变anArray的状态。如果我理解正确,我只想把指向二维数组的指针作为参数传入。函数还需要接受不同大小的数组。例如,[10][10][5][5]。我该怎么做?


有三种方法可以将二维数组传递给函数:

  • 参数是二维数组

    1
    2
    3
    4
    5
    6
    int array[10][10];
    void passFunc(int a[][10])
    {
        // ...
    }
    passFunc(array);
  • 参数是包含指针的数组

    1
    2
    3
    4
    5
    6
    7
    8
    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);
  • 参数是指向指针的指针

    1
    2
    3
    4
    5
    6
    7
    8
    9
    int **array;
    array = new int *[10];
    for(int i = 0; i <10; i++)
        array[i] = new int[10];
    void passFunc(int **a)
    {
        // ...
    }
    passFunc(array);

  • 固定尺寸

    1。通过引用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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;
        }
    }

    在C++中,通过引用传递数组而不丢失维度信息可能是最安全的,因为不必担心调用方传递不正确的维度(编译器不匹配时的标志)。但是,对于动态(FreeStore)数组,这是不可能的;它只适用于自动(通常是堆栈活动)数组,也就是说,在编译时应该知道维数。

    2。传递指针

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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;
        }    
    }

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

    可变尺寸

    这些是从C继承的,但安全性较低,编译器无法进行检查,从而确保调用者正在传递所需的维度。该函数只存储调用者作为维度传入的内容。因为不同长度的数组总是可以传递给它们,所以它们比上述的数组更灵活。

    要记住的是,没有这样的事情:将数组直接传递给C中的函数[而C++中它们可以作为引用传递(1)];(2)传递指向数组的指针,而不是数组本身。始终按原样传递数组将变为指针复制操作,这得益于数组衰减为指针的性质。

    三。传递(值)指向衰减类型的指针

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // 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;
        }
    }

    虽然允许使用int array[][10],但我不建议使用上面的语法,因为上面的语法清楚地表明,标识符array是指向10个整数数组的单个指针,而这个语法看起来像是一个二维数组,但与指向10个整数数组的指针相同。这里我们知道一行中元素的数量(即列大小,这里是10),但是行的数量是未知的,因此要作为参数传递。在这种情况下,有一些安全性,因为当传递指向第二维度不等于10的数组的指针时,编译器可以标记。第一个维度是变化的部分,可以省略。有关为什么只允许省略第一个维度的原因,请参见此处。

    4。将指针传递给指针

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // 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;
        }
    }

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

    注:(4)是最安全的选择,因为它几乎没有任何类型的检查和最不方便。不能合法地将一个二维数组传递给这个函数;C-FAQ谴责了执行int x[5][10]; process_pointer_2_pointer((int**)&x[0][0], 5, 10);的常见解决方法,因为它可能会由于数组扁平而导致不定义的行为。在这个方法中传递数组的正确方法给我们带来了不便,即我们需要一个额外的(代理)指针数组,其中每个元素指向实际数组的相应行;然后将这个代理传递给函数(见下文);所有这些都是为了完成与上述方法相同的工作。更安全,更干净,也许更快。

    下面是一个用于测试上述功能的驱动程序:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    #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**
    }


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    template <size_t size_x, size_t size_y>
    void func(double (&arr)[size_x][size_y])
    {
        printf("%p
    "
    , &arr);
    }

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

        printf("%p
    %p

    "
    , &a1, &a2);
        func(a1);
        func(a2);

        return 0;
    }

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


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

    1
    2
    3
    4
    5
    6
    template<int R, int C>
    void myFunction(double (&myArray)[R][C])
    {
        myArray[x][y] = 5;
        etc...
    }

    然后,通过R和C可以同时获得两个维度大小。将为每个数组大小创建不同的函数,因此如果函数很大,并且使用各种不同的数组大小来调用它,那么这可能会很昂贵。但是,您可以将它用作类似以下函数的包装器:

    1
    2
    3
    4
    5
    void myFunction(double * arr, int R, int C)
    {
        arr[x * C + y] = 5;
        etc...
    }

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

    1
    2
    3
    4
    5
    template<int C, int R>
    void myFunction(double (&myArray)[R][C])
    {
        myFunction(*myArray, R, C);
    }


    令人惊讶的是,还没有人提到这一点,但是您可以简单地在任何支持[][]语义的2d上创建模板。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    template <typename TwoD>
    void myFunction(TwoD& myArray){
         myArray[x][y] = 5;
         etc...
    }

    // call with
    double anArray[10][10];
    myFunction(anArray);

    它可以与任何二维"类似数组"的数据结构(如std::vector>或用户定义的类型)一起工作,以最大限度地提高代码重用率。


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

    1
    2
    void f(double p[][10]) {
    }

    但是,这不会让您传递最后一个维度不是10的数组。

    C++中最好的解决方案是使用EDCOX1〔1〕:它几乎是高效的,而且更方便。


    你可以这样做…

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    #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...
    "
    ;
        for(int i=0;i<rows;i++){
            for(int j=0;j<cols;j++){
                cout<<*(a+ i*rows + j)<<" ";
            }
        cout<<"
    "
    ;
        }
    }

    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;
    }

    你的输出如下…

    1
    2
    11.5  12.5
    13.5  14.5


    一维数组衰减为指向数组中第一个元素的指针指针。当二维数组衰减到指向第一行的指针时。所以,函数原型应该是-

    1
    void myFunction(double (*myArray) [10]);

    我更喜欢std::vector而不是原始数组。


    这是向量矩阵的向量示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    #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;
    }

    输出:

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

    0 0 0 0
    0 0 0 0
    0 0 0 0

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

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

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      #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;
      }
    • 通过这种方式使用双指针,我们还可以对二维数组进行类型转换。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
        #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("
    "
    );
          }
       }

      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;
       }

    传递多维数组的一个重要问题是:

    • 无需指定First array dimension
    • 必须指定Second(any any further)dimension

    1.当只有第二个维度全局可用时(宏或全局常量)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    `const int N = 3;

    `void print(int arr[][N], int m)
    {
    int i, j;
    for (i = 0; i < m; i++)
      for (j = 0; j < N; j++)
        printf("%d", arr[i][j]);
    }`

    int main()
    {
    int arr[][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
    print(arr, 3);
    return 0;
    }`

    2.使用单指针:在这个方法中,我们必须在传递给函数时对二维数组进行类型转换。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    `void print(int *arr, int m, int n)
    {
    int i, j;
    for (i = 0; i < m; i++)
      for (j = 0; j < n; j++)
        printf("%d", *((arr+i*n) + j));
     }

    `int main()
    {
    int arr[][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
    int m = 3, n = 3;

    // We can also use"print(&arr[0][0], m, n);"
    print((int *)arr, m, n);
    return 0;
    }`


    你可以在C++中使用模板工具来完成这个任务。我做了这样的事:

    1
    2
    3
    4
    template<typename T, size_t col>
    T process(T a[][col], size_t row) {
    ...
    }

    这种方法的问题在于,对于您提供的每一个col值,都会使用模板实例化一个新的函数定义。所以,

    1
    2
    3
    int some_mat[3][3], another_mat[4,5];
    process(some_mat, 3);
    process(another_mat, 4);

    将模板实例化两次以生成两个函数定义(一个定义col=3,另一个定义col=5)。