【问题标题】:Passing array with unknown size to function将未知大小的数组传递给函数
【发布时间】:2013-08-20 02:34:58
【问题描述】:

假设我有一个名为MyFunction(int myArray[][]) 的函数,它执行一些数组操作。

如果我这样写参数列表,编译器会抱怨它需要在编译时知道数组的大小。有没有办法重写参数列表,以便我可以将任意大小的数组传递给函数?

我的数组大小由一个类中的两个static const ints 定义,但编译器不会接受MyFunction(int myArray[Board::ROWS][Board::COLS]) 之类的东西。

如果我可以将数组转换为向量,然后将向量传递给MyFunction,会怎样?我可以使用单行转换还是必须手动进行转换?

【问题讨论】:

  • 你要传递什么类型的数组?
  • @Dadam:我有Piece 指针的二维数组。 Piece 是我创建的一个类。
  • 是 Piece array[X][Y] 还是 Piece **array?

标签: c++ arrays


【解决方案1】:

在 C++ 语言中,多维数组声明必须始终包含所有大小,可能第一个除外。因此,您尝试做的事情是不可能的。如果不明确指定大小,则不能声明内置多维数组类型的参数。

如果您需要将运行时大小的多维数组传递给函数,您可以忘记使用内置多维数组类型。一种可能的解决方法是使用“模拟”多维数组(指向其他一维数组的指针的一维数组;或通过重新计算索引来模拟多维数组的普通一维数组)。

【讨论】:

  • 它没有提到矢量,但是,这个答案适用于所提出的问题,但是在指导初学者(这是一个初学者问题)时,最好引导他们找到问题的实际答案简单地接受他们所要求的表面价值。
  • @M2tmM:对于多维数组,向量通常不是一个热门的解决方案。
  • @jalf: gamedev.net/community/forums/… boost::multi_array 在某些情况下可能会更好。
  • 我会说,在大多数情况下,二维向量可能就很好(和标准)。
【解决方案2】:

在 C++ 中使用 std::vector 对数组建模,除非您有使用数组的特定原因。

一个填充为 0 的 3x2 向量被初始化为“myArray”的示例:

vector< vector<int> > myArray(3, vector<int>(2,0));

传递这个结构是微不足道的,你不需要搞砸传递长度(因为它会跟踪):

void myFunction(vector< vector<int> > &myArray) {
    for(size_t x = 0;x < myArray.length();++x){
        for(size_t y = 0;y < myArray[x].length();++y){
            cout << myArray[x][y] << " ";
        }
        cout << endl;
    }
}

您也可以使用迭代器对其进行迭代:

void myFunction(vector< vector<int> > &myArray) {
    for(vector< vector<int> >::iterator x = myArray.begin();x != myArray.end();++x){
        for(vector<int>::iterator y = x->begin();y != x->end();++y){
            cout << *y << " ";
        }
        cout << endl;
    }
}

在 C++0x 中,您可以使用 auto 关键字来清理向量迭代器解决方案:

void myFunction(vector< vector<int> > &myArray) {
    for(auto x = myArray.begin();x != myArray.end();++x){
        for(auto y = x->begin();y != x->end();++y){
            cout << *y << " ";
        }
        cout << endl;
    }
}

在 c++0x 中 for_each 可以通过 lambdas 实现

void myFunction(vector< vector<int> > &myArray) {
    for_each(myArray.begin(), myArray.end(), [](const vector<int> &x){
        for_each(x->begin(), x->end(), [](int value){
            cout << value << " ";
        });
        cout << endl;
    });
}

或者c++0x中基于范围的for循环:

void myFunction(vector< vector<int> > &myArray) {
    for(auto x : myArray){
        for(auto y : *x){
            cout << *y << " ";
        }
        cout << endl;
    }
}

*我现在不在编译器附近,也没有测试过这些,请随时纠正我的例子。


如果您在编译时知道数组的大小,则可以执行以下操作(假设大小为 [x][10]):

MyFunction(int myArray[][10])

如果你需要传入一个可变长度数组(动态分配或者可能只是一个需要不同大小数组的函数),那么你需要处理pointers

并且作为这个答案状态的cmets:

boost::multiarray 可能是合适的,因为它可以更有效地建模多维数组。向量向量可能会对关键路径代码的性能产生影响,但在典型情况下,您可能不会注意到问题。

【讨论】:

  • std::arrayboost::array
  • boost::multiarray 因为这应该是一个二维数组:)
【解决方案3】:

将其作为指针传递,并将维度作为参数。

void foo(int *array, int width, int height) {
    // initialize xPos and yPos
    assert(xPos >= 0 && xPos < width);
    assert(yPos >= 0 && yPos < height);
    int value = array[yPos * width + xPos];
}

这是假设您有一个简单的二维数组,例如 int x[50][50]

【讨论】:

  • 您应该在回答中提到您需要将第一个参数提供为&amp;x[0][0]
【解决方案4】:

已经有一组最常见的答案了另一种基于原生数组的解决方案——请注意,如果可能,您应该使用更高级别的抽象。

无论如何:

template <std::size_t rows, std::size_t cols>
void function( int (&array)[rows][cols] )
{
   // ...
}

此解决方案使用数组的引用(注意&amp;array 周围的括号集),而不是使用按值传递语法。这会强制编译器不要将数组衰减为指针。然后这两种尺寸(可以作为编译时常量提供)可以定义为模板参数,编译器会为你扣除尺寸。

注意:您在问题中提到大小实际上是静态常量,您应该能够在函数签名中使用它们 if 您在类声明中提供值:

struct test {
   static const int rows = 25;
   static const int cols = 80;
};
void function( int *array[80], int rows ) {
   // ...
}

请注意,在签名中,我更喜欢将二维数组更改为指向数组的指针。原因是 this 是编译器解释的任何一种方式,并且这种方式很明显不能保证函数的调用者将传递正好 25 行的数组(编译器将 not 强制执行),因此很明显需要调用者传递行数的第二个整数参数。

【讨论】:

  • 非常简洁的模板元编程技巧!但不适用于 rows/cols == 0。
  • @malat:我不会称之为元编程,它是简单的类型推导,没有神奇的元函数来推断任何东西。它不适用于行/列为 0 的事实是由于无法在语言中拥有维度为 0 的数组。由于不允许使用大小为 0 的数组,因此模板不接受它们不是限制:)
【解决方案5】:

你不能像这样传递任意大小;编译器不知道如何生成指针算法。你可以这样做:

MyFunction(int myArray[][N])

或者你可以这样做:

MyFunction(int *p, int M, int N)

但您在调用它时必须获取第一个元素的地址(即MyFunction(&amp;arr[0][0], M, N)

您可以通过使用容器类来解决 C++ 中的所有这些问题; std::vector 将是一个不错的起点。

【讨论】:

    【解决方案6】:

    编译器抱怨是因为它需要知道除第一个维度之外的所有维度的大小,才能对数组中的元素进行寻址。比如下面的代码:

    int array[M][N];
    // ...
    array[i][j] = 0;
    

    为了寻址元素,编译器生成如下内容:

    *(array+(i*N+j)) = 0;
    

    因此,您需要像这样重写您的签名:

    MyFunction(int array[][N])
    

    在这种情况下,您将被固定维度卡住,或者使用更通用的解决方案,例如(自定义)动态 2D 数组类或vector&lt;vector&lt;int&gt; &gt;

    【讨论】:

      【解决方案7】:
      1. 使用vector&lt;vector&lt;int&gt; &gt;(如果不能保证底层存储是连续的,这将是作弊)。

      2. 使用指向element-of-array (int*) 的指针和size (M*N) 参数。这里是龙。

      【讨论】:

        【解决方案8】:

        首先,让我们看看为什么编译器会抱怨。

        如果一个数组被定义为int arr[ ROWS ][ COLS ];,那么任何数组符号arr[ i ][ j ]都可以被转换为指针符号

        *( arr + i * COLS + j )
        

        观察到表达式只需要COLS,不需要ROWS。所以,数组定义可以等价地写成

        int arr [][ COLS ];
        

        但是,不接受第二个维度。更多细节,请阅读here

        现在,关于你的问题:

        有没有办法改写 参数列表,以便我可以传递一个 函数的任何大小的数组?

        是的,也许您可​​以使用指针,例如MyFunction( int * arr );。但是,想一想,MyFunction() 怎么知道在哪里停止访问数组?为了解决这个问题,您需要另一个数组长度参数,例如MyFunction( int * arr, size_t arrSize );

        【讨论】:

          【解决方案9】:

          是的:MyFunction(int **myArray);

          不过要小心。你最好知道你在做什么。这将只接受一个 int 指针数组。

          由于您尝试传递一个数组数组,因此您需要一个常量表达式作为维度之一:

          MyFunction(int myArray[][COLS]);

          您需要在编译时拥有COLS

          我建议改用vector

          【讨论】:

          • 这不等于用户想要的:int 的二维数组不能作为指向 int 的指针传递。
          【解决方案10】:

          传递一个指针并自己进行索引,或者改用 Matrix 类。

          【讨论】:

            【解决方案11】:

            是的 - 只需将其作为指针传递:

            MyFunction(int** someArray)
            

            缺点是您可能还需要传递数组的长度

            【讨论】:

            • 这并不像听起来那么简单。提问者大概有int a[M][N] 之类的东西,并想将a 传递给他的函数。为了进行双重解引用,他必须首先创建一个指向行的指针数组。
            • 其实一旦进入方法他就可以使用 a[x][y] 来获取正确的项目
            【解决方案12】:

            使用MyFunction(int *myArray[])
            如果使用MyFunction(int **myArray) 传递int someArray[X][Y],程序将崩溃。
            编辑:不要使用第一行,它在 cmets 中有解释。

            【讨论】:

            • 你是对的,我的错。第一行适用于定义为指向另一个数组的指针数组的二维数组。
            • 第二行也是如此(它们完全相同)。
            【解决方案13】:

            我不了解 C++,但是 C99 标准引入了可变长度数组。

            所以这可以在支持 C99 的编译器中工作:

            void func(int rows, int cols, double[rows][cols] matrix) {
                for (int r = 0; r < rows; r++) {
                    for (int c = 0; c < cols; c++) {
                        printf("%f", matrix[r][c]);
                    }
                }
            }
            

            请注意,大小参数位于数组之前。实际上,在编译时只需要知道列数,所以这也是有效的:

            void func(int rows, int cols, double[][cols] matrix)
            

            对于三个或更多维度,除第一个维度之外的所有维度都必须具有已知大小。 ArunSaha 链接到的答案解释了原因。

            老实说,我不知道 C++ 是否支持变长数组,所以这可能有效,也可能无效。无论哪种情况,您也可以考虑将数组封装在某种矩阵类中。

            编辑:从您的编辑看来,C++ 可能不支持此功能。矩阵类可能是要走的路。 (或者 std::vector 如果您不介意内存可能不会连续分配。)

            【讨论】:

            • C++ 中不支持可变长度数组。您可以做的最接近的方法是在编译时通过模板,让编译器推断出大小。
            • 你是对的,看起来一些 C99 更改将被添加到 C++0x,但不是 VLA:www2.research.att.com/~bs/C++0xFAQ.html#C99
            【解决方案14】:

            不要传递数组,这是一个实现细节。通过董事会

            MyFunction(Board theBoard)
            {
                ...
            }
            

            【讨论】:

              【解决方案15】:

              在 C++0x 中,you can use std::initializer_list&lt;...&gt; 完成此操作:

              MyFunction(std::initializer_list<std::initializer_list<int>> myArray);
              

              并像这样使用它(我想)(使用range based for syntax):

              for (const std::initializer_list<int> &subArray: myArray)
              {
                  for (int value: subArray)
                  {
                      // fun with value!
                  }
              }
              

              【讨论】:

                【解决方案16】:

                实际上,我的数组大小由一个类中的两个static const ints 定义,但编译器不会接受MyFunction(int myArray[Board::ROWS][Board::COLS]) 之类的东西。

                这很奇怪,对我来说效果很好:

                struct Board
                {
                    static const int ROWS = 6;
                    static const int COLS = 7;
                };
                
                void MyFunction(int myArray[Board::ROWS][Board::COLS])
                {
                }
                

                也许 ROWS 和 COLS 是私有的?你能给我们看一些代码吗?

                【讨论】:

                  【解决方案17】:

                  在 C++ 中,使用内置数组类型会立即失败。您可以使用 boost::/std:: 数组数组或数组向量。原始数组没有任何实际用途

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 2012-04-27
                    • 2015-04-24
                    • 1970-01-01
                    • 1970-01-01
                    • 2022-11-05
                    • 2013-03-12
                    • 2020-04-11
                    • 1970-01-01
                    相关资源
                    最近更新 更多