【问题标题】:int[n][m], where n and m are known at runtimeint[n][m],其中 n 和 m 在运行时已知
【发布时间】:2014-10-22 11:48:01
【问题描述】:

我经常需要在编译时创建一个宽度和高度(假设它们是 n 和 m)未知的 2D 数组,通常我会这样写:

vector<int> arr(n * m);

我手动访问元素:

arr[j * m + i] 

最近有人告诉我,我可以改写:

int arr[n][m] // n and m still only known at runtime.

所以这里有两个问题:

  1. C++ 标准是否允许这种行为?
  2. 我应该如何将这样的数组传递给函数? g++ 报告 arr 的类型为 int (*)[n],但同样,n 是动态的,并且在声明它的函数之外是未知的 (main)。

【问题讨论】:

  • 1.没有。2。如果对 1. 的回答是“否”,这有关系吗?
  • 这是非标准行为(您可以更改编译器选项以禁止非标准行为)。做你对向量所做的事情,但只需将它包装在一个类中。
  • 或使用vector&lt;vector&lt;int&gt;&gt;
  • vector 解决方案似乎是最好的解决方案...
  • @Quest 你确定吗?你有报价吗?

标签: c++ arrays multidimensional-array dynamic-arrays


【解决方案1】:

您询问的功能(尺寸仅在运行时才知道)是 C++ 的非标准扩展,但 C.99 的标准扩展(在 C.11 中成为可选功能)。该功能称为variable-length array (VLA),链接是GCC的文档。

如果您使用的是 GCC,那么您需要将数组的长度作为参数传递给函数。

void foo (int m, int arr[][m]) {
    //...
}

然而,编译器或文档中似乎存在错误,因为上述函数原型语法仅在编译 C 代码时有效,而不是 C++(如gcc 版本 4.8.2)。我发现的唯一解决方法是使用 void * 参数,并将其转换为函数体:

int foo_workaround (int m, void *x)
{
    int (*arr)[m] = static_cast<int (*)[m]>(x);
    //...
}

如果您不想依赖编译器扩展,还有其他解决方案。如果您不介意为每一行单独分配,则可以使用向量的向量,例如:

std::vector<std::vector<int> > arr(n, std::vector<int>(m));

但是,如果您想要一个像您在自己的示例中演示的那样的单个分配块,那么最好围绕 vector 创建一个包装器类,以便为您提供类似二维的语法。

template <typename T>
class vector2d {

    int n_;
    int m_;
    std::vector<T> vec_;

    template <typename I>
    class vector2d_ref {
        typedef std::iterator_traits<I> TRAITS;
        typedef typename TRAITS::value_type R_TYPE;
        template <typename> friend class vector2d;
        I p_;
        vector2d_ref (I p) : p_(p) {}
    public:
        R_TYPE & operator [] (int j) { return *(p_+j); }
    };

    typedef std::vector<T> VEC;
    typedef vector2d_ref<typename VEC::iterator> REF;
    typedef vector2d_ref<typename VEC::const_iterator> CREF;

    template <typename I> 
    vector2d_ref<I> ref (I p, int i) { return p + (i * m_); }

public:

    vector2d (int n, int m) : n_(n), m_(m), vec_(n*m) {}
    REF operator [] (int i) { return ref(vec_.begin(), i); }
    CREF operator [] (int i) const { return ref(vec_.begin(), i); }

};

包装器的operator[] 返回一个中间对象,该对象还重载operator[] 以在使用包装器时允许二维数组语法。

    vector2d<int> v(n, m);
    v[i][j] = 7;
    std::cout << v[i][j] << '\n';

【讨论】:

  • 我正在使用 g++,编译失败(使用 -std=c++11)对于以下函数(这里只是原型): int sum(int n, int[][n] m ); int sum(int n, int[n][n] m);原因:“在 ] 标记之前使用函数体外部的参数”。怎么了?
  • 改用-std=gnu++11
  • 我不确定我是否正确阅读了您的代码。 ')'之前有什么额外的'm'?
  • m 表示“矩阵”,数组的名称。
  • 哦,不是。在 [][n] 之前移动 arr 名称不固定。
【解决方案2】:

为什么没有std::vectorstd::vector

std::vector<std::vector<int> > arr(n, std::vector<int>(m));

访问一个项目然后变成:

std::cout << "(2,1) = " << arr[2][1] << std::endl;

【讨论】:

  • 它不是简单的二维数组,它是数组的数组。但是这个方案也是可以接受的。
  • @Jost,如果你声明 int a[10][10] 它在内部将是 int a[100]
  • @cdkrot 请查看this question上的第一个答案
  • 是的,这就是我所说的,元素存储在连续数组中。关于传入函数,在这个问题上回答一个应该可以,但我还没有测试过。
  • 我没有对任何人投反对票,std::vector 将使用动态内存分配,它不会以这种方式运行,它将像数组一样,包含指向其他数组的指针(可能在其他地方)
【解决方案3】:

std::vector 中的 std::vector(来自 #include &lt;vector&gt;)与二维数组的作用相同:

int n = 10, m = 10; //vector dimensions
std::vector<std::vector<int>> arr(n, std::vector<int>(m)); //Create 2D vector (vector will be stored as "std::vector<int> arr(n * m);

//you can get values from 2D vector the same way that you can for arrays
int a = 5, b = 5, value = 12345;
arr[a][b] = 12345;
std::cout << "The element at position (" << a << ", " << b << ") is " << arr[a][b] << "." << std::endl;

输出:

The element at position (5, 5) is 12345.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-04
    相关资源
    最近更新 更多