首页 文章

将2D数组传递给C函数

提问于
浏览
243

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

到目前为止我有这个:

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

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

double anArray[10][10];

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

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

12 回答

  • 137

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

    • 该参数是一个2D数组
    int array[10][10];
    void passFunc(int a[][10])
    {
        // ...
    }
    passFunc(array);
    
    • 该参数是一个包含指针的数组
    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);
    
    • 该参数是指向指针的指针
    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. Pass by reference

    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)数组可能不需要't worry about the caller passing an incorrect dimension (compiler flags when mismatching). However, this isn't;它仅适用于自动(usually stack-living)数组,即维度应在编译时知道 .

    2. Pass by pointer

    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) 正在传递指向数组的指针而不是数组本身 . 始终按原样传递数组成为指针复制操作,由array's nature of decaying into a pointer促进 .

    3. Pass by (value) a pointer to the decayed type

    // 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的数组的指针时,可以标记 . 第一个维度是变化的部分,可以省略 . See here for the rationale为什么只允许省略第一个维度 .

    4. Pass by pointer to a pointer

    // 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列,即使这样也需要行数 . 在任何情况下,编译器都不会标记任何长度/大小违规(它只检查传递的类型是否是指向指针的指针),因此需要行和列计数作为参数在这里有意义 .

    Note: (4)是最安全的选择,因为它几乎没有任何类型检查和最不方便 . 人们无法合法地将2D数组传递给此函数; C-FAQ condemns由于数组扁平化而 int x[5][10]; process_pointer_2_pointer((int**)&x[0][0], 5, 10); 执行 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**
    }
    
  • 7

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

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

  • 10

    您可以像这样创建一个函数模板:

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

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

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

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

    template<int C, int R>
    void myFunction(double (&myArray)[R][C])
    {
        myFunction(*myArray, R, C);
    }
    
  • 10

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

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

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

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

  • 0

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

    template <typename TwoD>
    void myFunction(TwoD& myArray){
         myArray[x][y] = 5;
         etc...
    }
    
    // call with
    double anArray[10][10];
    myFunction(anArray);
    

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

  • 19

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

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

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

  • 7

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

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

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

    11.5  12.5
    13.5  14.5
    
  • 345

    传递多维数组的一个重要方面是:

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

    1.When only second dimension is available globally (either as a macro or as a global constant)

    `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.Using a single pointer :在这种方法中,我们必须在传递给函数时对2D数组进行类型转换 .

    `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;
    }`
    
  • 0

    这是矢量矩阵示例的向量

    #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 8
    9 1 2 3
    
    0 0 0 0
    0 0 0 0
    0 0 0 0
    
  • 1

    您可以在C中使用模板工具来执行此操作 . 我做了这样的事情:

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

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

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

    实例化模板两次以产生2个函数定义(一个col = 3,另一个col = 5) .

  • 35

    我们可以使用几种方法将2D数组传递给函数:

    • Using single pointer 我们必须对2D数组进行类型转换 .
    #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;
    }
    
    • Using double pointer 通过这种方式,我们还对2d数组进行了类型转换
    #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;
       }
    

相关问题