std::vector 和多维数组的连续内存

Posted

技术标签:

【中文标题】std::vector 和多维数组的连续内存【英文标题】:std::vector and contiguous memory of multidimensional arrays 【发布时间】:2011-09-21 20:23:06 【问题描述】:

我知道标准不会强制std::vector分配连续的内存块,但所有的实现都遵循这一点。

假设我希望创建一个多维静态数组的向量。为简单起见,考虑二维和长度为 N 的向量。也就是说,我希望创建一个包含 N 个元素的向量,例如 int[5]

我可以确定所有 N*5 整数现在在内存中都是连续的吗?这样我原则上可以通过知道第一个元素的地址来访问所有整数?这个实现是否依赖?

作为参考,我目前在连续内存块中创建 2D 数组的方式是首先创建一个长度为 N 的 float* (动态)数组,将所有 N*5 个浮点数分配到一个数组中,然后复制每个数组的地址float* 的第一个数组中的第 5 个元素。

【问题讨论】:

我知道标准不强制std::vector分配连续的内存块 — It does, starting from C++03. @KennyTM:不知道它不在 C++98 中。谢谢。我想这仍然是一个实际要求,以满足元素访问的规定操作复杂性要求,对吧?就像std::string 在实践中一直有连续的元素存储一样,尽管直到 C++0x 才明确规定它。 【参考方案1】:

标准确实要求std::vector的内存是 连续的。另一方面,如果你写这样的东西:

std::vector<std::vector<double> > v;

全局内存(所有v[i][j])将不连续。这 创建二维数组的常用方法是使用单个

std::vector<double> v;

并完全按照您对float 的建议来计算索引。 (您也可以使用地址创建第二个std::vector&lt;float*&gt; 如果你想。但是,我总是只是重新计算索引。)

【讨论】:

+1,对于初始方法,您可以在 C++FAQ lite 中考虑这个example。 大卫:很好的参考。每个喜欢“2D Arrays”的人都应该阅读它和以下条目,直到他们可以在睡梦中重复它;-)【参考方案2】:

根据 C++ 标准,保证向量的元素是连续的。 标准引用如下:

来自 n2798(C++0x 草案):

23.2.6 类模板向量[vector]

1 向量是支持随机访问迭代器的序列容器。此外,它还支持(摊销)恒定时间的最后插入和擦除操作;在中间插入和擦除需要线性时间。存储管理是自动处理的,但可以给出提示以提高效率。向量的元素是连续存储的,这意味着如果 v 是一个向量,其中 T 是除 bool 之外的某种类型,那么对于所有 0

C++03 标准 (23.2.4.1):

向量的元素是连续存储的,这意味着如果 v 是一个向量,其中 T 是 bool 以外的某种类型,那么它对所有的都服从恒等式 &v[n] == &v[0] + n 0

另外,请参阅 here Herb Sutter 对此的看法。

【讨论】:

万岁 std::vector :D :D :D 但是C++0X还不是官方标准。【参考方案3】:

正如@Als 已经指出的那样,是的,std::vector(现在)保证连续分配。但是,我不会用指针数组模拟二维矩阵。相反,我会推荐两种方法之一。 (到目前为止)更简单的方法是仅使用 operator() 进行下标,然后进行乘法以将 2D 输入转换为向量中的线性地址:

template <class T>
class matrix2D  
     std::vector<T> data;
     int columns;
public:
     T &operator()(int x, int y) 
         return data[y * columns + x];
     

     matrix2D(int x, int y) : data(x*y), columns(x) 
;

如果出于某种原因,您想使用matrix[a][b] 样式寻址,您可以使用代理类来处理转换。虽然它是用于 3D 矩阵而不是 2D,但我在 previous answer 上发布了该技术的演示。

【讨论】:

【参考方案4】:

作为参考,我目前在连续内存块中创建 2D 数组的方式是首先创建一个长度为 N 的 float* (动态)数组,将所有 N*5 个浮点数分配到一个数组中,然后复制每个数组的地址float* 的第一个数组中的第 5 个元素。

这不是二维数组,而是指针数组。如果你想要一个真正的二维数组,可以这样做:

float (*p)[5] = new float[N][5];

p [0] [0] = 42;   // access first element
p[N-1][4] = 42;   // access last element

delete[] p;

注意只有一个分配。我可以建议阅读更多关于using arrays in C++ 的内容吗?

【讨论】:

std::vector&lt;std::array&lt;float, 5&gt; &gt; @Tom:嗯,看起来很好吃!【参考方案5】:

在底层,一个向量可能看起来大概像(p-code):

class vector<T> 
    T      *data;
    size_t  s;
;

现在如果你做一个vector&lt;vector&lt;T&gt; &gt;,就会有这样的布局

vector<vector<T>> --> data 
    vector<T>,
    vector<T>,
    vector<T>
;

或以“内联”形式

vector<vector<T>> --> data 
    data0, s0,
    data1, s1,
    data2, s2
;

是的,向量向量因此使用连续内存,但不,不是你想要的。它很可能存储一个指向外部位置的指针数组(和一些其他变量)。

标准只要求向量的数据是连续的,不要求向量作为一个整体。

【讨论】:

【参考方案6】:

创建一个简单的类,如您所说,一个 2D 数组,类似于:

template <class T> 2DArray 
private:
    T *m_data;
    int m_stride;
public:
    2DArray(int dimY, int dimX) : m_stride(dimX) : m_data(new[] T[dimX * dimY]) 
    ~2DArray()  delete[] m_data; 
    T* operator[](int row)  return m_data + m_stride * row; 

可以这样使用:

2DArray<int> myArray(30,20);

for (int i = 0; i < 30; i++)
    for (int j = 0; j < 20; j++)
        myArray[i][j] = i + j;

或者甚至将&amp;myArray[0][0] 作为地址传递给采用某种“平面缓冲区”的低级函数。

但正如您所见,它改变了幼稚的期望,因为它是 myarray[y][x]

一般来说,如果您与需要某种经典 C 风格平面数组的代码交互,那么为什么不直接使用呢?

编辑:如前所述,上面是简单。没有任何边界检查尝试。就像,“一个数组”。

【讨论】:

以上是关于std::vector 和多维数组的连续内存的主要内容,如果未能解决你的问题,请参考以下文章

连续数组多维表示的快速元素访问

多维数组

将标量的多维向量(张量)写入 C++ 文件

多维数组指向多维数组

多维容器按列组合元素

将嵌套 C++ 向量作为内置样式的多维数组传递