我的问题很简单:std::vector
元素是否保证是连续的?换句话说,我可以将指向 std::vector
的第一个元素的指针用作 C 数组吗?
如果我没记错的话,C++ 标准并没有做出这样的保证。但是,std::vector
要求是,如果元素不连续,几乎不可能满足它们。
有人可以澄清一下吗?
例子:
std::vector<int> values;
// ... fill up values
if( !values.empty() )
{
int *array = &values[0];
for( int i = 0; i < values.size(); ++i )
{
int v = array[i];
// do something with 'v'
}
}
if
块内改变 values
,你就有麻烦了。不过,我不知道你的问题的答案,所以我只是发表评论。 :)
values
的调用,特别是改变其大小的调用(例如,push_back()
),可能会提示重新分配底层向量,从而使复制到 array
的指针无效。这与使用 vector::iterator 而不是指向向量的指针背后的原理相同。 :)
这在 C++98 标准中被遗漏,但后来作为 TR 的一部分添加。即将发布的 C++0x 标准当然会将此作为要求。
来自 n2798(C++0x 草案):
23.2.6 类模板向量[vector] 1 向量是支持随机访问迭代器的序列容器。此外,它还支持(摊销)恒定时间的最后插入和擦除操作;在中间插入和擦除需要线性时间。存储管理是自动处理的,但可以给出提示以提高效率。向量的元素是连续存储的,这意味着如果 v 是一个向量,其中 T 是除 bool 之外的某种类型,那么对于所有 0 <= n < v,它都遵循恒等式 &v[n] == &v[0] + n 。尺寸()。
正如其他答案所指出的那样,向量的内容保证是连续的(布尔的怪异除外)。
我想添加的评论是,如果您对向量执行插入或删除操作,这可能导致向量重新分配其内存,那么您将导致所有保存的指针和迭代器无效。
vector.push_back(3)
是一个插入,因此它使迭代器无效。 2. 我不希望 swap(vector[3], vector[4])
使迭代器无效,因为没有分配新内存,但我没有引用来备份它。 3. swap(vector_1, vector_2)
很有趣。在此之后我可能不会信任迭代器,但我不确定它们是否继续有效。
实际上,该标准确实保证 vector
在内存中是连续的,并且可以将 &a[0]
传递给需要数组的 C
函数。
此规则的例外是 vector<bool>
,它每个 bool
仅使用一位,因此尽管它确实具有连续内存但不能用作 bool*
(这被广泛认为是错误的优化和错误) .
顺便说一句,你为什么不使用迭代器?这就是他们的目的。
正如其他人已经说过的那样,vector
在内部使用了一个连续的对象数组。只要调用任何非常量成员函数 IIRC,指向该数组的指针都应被视为无效。
但是,有一个例外!!
vector<bool>
有一个专门用于节省空间的实现,因此每个 bool 只使用一位。底层数组不是一个连续的 bool 数组,并且 vector<bool>
上的数组算术不像 vector<T>
那样工作。
(我想这也可能适用于向量的任何特化,因为我们总是可以实现一个新的。但是,std::vector<bool>
是唯一的,错误的,标准的特化,简单的指针算术不起作用。)
std::vector
,并且所有其他向量都需要使用连续存储。因此,std::vector<bool>
是(幸运的是)唯一奇怪的标准向量。 (我强烈认为应该弃用这种专业化,并用具有几乎相同功能的 std::dynamic_bitset
代替。这不是一个糟糕的数据结构,它只是不是一个向量。)
我找到了这个线程,因为我有一个用例,其中使用连续内存的向量是一个优势。
我正在学习如何在 OpenGL 中使用顶点缓冲区对象。我创建了一个包装类来包含缓冲区逻辑,所以我需要做的就是传递一个浮点数组和一些配置值来创建缓冲区。我希望能够根据用户输入从函数生成缓冲区,因此在编译时长度是未知的。做这样的事情将是最简单的解决方案:
void generate(std::vector<float> v)
{
float f = generate_next_float();
v.push_back(f);
}
现在我可以将向量的浮点数作为数组传递给 OpenGL 的缓冲区相关函数。这也消除了使用 sizeof 来确定数组长度的需要。
这比分配一个巨大的数组来存储浮点数并希望我把它做得足够大,或者用连续存储创建我自己的动态数组要好得多。
v
而不是 v
本身的指针?因为单独传递 v
将导致在函数内部进行复制,该副本将在函数结束后不复存在。因此,您将某些东西推到向量上,只是为了在函数结束时删除向量。
向量容器被实现为动态数组;与常规数组一样,向量容器的元素存储在连续的存储位置,这意味着不仅可以使用迭代器访问它们的元素,还可以使用指向元素的常规指针的偏移量来访问它们的元素。
是的, std::vector 的元素保证是连续的。
不定期副业成功案例分享
std::vector
的实例是连续的。例如:在std::vector<std::vector<int>> v
中,元素v[0]
、v[1]
、...随后存储在内存中,但不保证元素v[0].back()
和v[1].front()
。