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<float*>
如果你想。但是,我总是只是重新计算索引。)
【讨论】:
+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正如@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<std::array<float, 5> >
@Tom:嗯,看起来很好吃!【参考方案5】:
在底层,一个向量可能看起来大概像(p-code):
class vector<T>
T *data;
size_t s;
;
现在如果你做一个vector<vector<T> >
,就会有这样的布局
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;
或者甚至将&myArray[0][0]
作为地址传递给采用某种“平面缓冲区”的低级函数。
但正如您所见,它改变了幼稚的期望,因为它是 myarray[y][x]
。
一般来说,如果您与需要某种经典 C 风格平面数组的代码交互,那么为什么不直接使用呢?
编辑:如前所述,上面是简单。没有任何边界检查尝试。就像,“一个数组”。
【讨论】:
以上是关于std::vector 和多维数组的连续内存的主要内容,如果未能解决你的问题,请参考以下文章