std::vector 元素是不是保证是连续的?
Posted
技术标签:
【中文标题】std::vector 元素是不是保证是连续的?【英文标题】:Are std::vector elements guaranteed to be contiguous?std::vector 元素是否保证是连续的? 【发布时间】:2009-05-11 17:38:05 【问题描述】:我的问题很简单: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
就会有麻烦。不过,我不知道你的问题的答案,所以我只是发表评论。 :)
@Greg:什么麻烦——你能详细说明一下吗?
我想他的意思是推送新值可能会触发“realloc”,这会导致数组变得无效。
改变values
的调用,特别是改变其大小的调用(例如,push_back()
),可能会提示重新分配底层向量,从而使复制到array
的指针无效。这与使用 vector::iterator 而不是指向向量的指针背后的原理相同。 :)
是的,我把 ``'s 放在值周围是为了表明我在谈论类本身,而不是其中包含的值。 :) 不幸的命名等等。我不认为在这个问题相关的一般情况下这真的是一个问题 - 为什么有人会抓住一个指向内存的指针,然后开始使用向量而不是使用指针?愚蠢。
【参考方案1】:
这在 C++98 标准中被遗漏,但后来作为 TR 的一部分添加。即将发布的 C++0x 标准当然会包含这一要求。
来自 n2798(C++0x 草案):
23.2.6 类模板向量[vector]
1 向量是支持随机访问迭代器的序列容器。此外,它支持(摊销) 最后的恒定时间插入和擦除操作;在中间插入和擦除需要线性时间。贮存 管理是自动处理的,但可以给出提示以提高效率。一个元素 向量是连续存储的,这意味着如果 v 是一个向量,而 T 是其他类型 比布尔值,那么它服从所有 0
【讨论】:
这在 ISO 14882,第 2 版中也有说明:第 23.2.4 节 [lib.vector]:“向量的元素是连续存储的,这意味着如果 v 是向量std::vector
的实例是连续的。例如:在std::vector<std::vector<int>> v
中,元素v[0]
,v[1]
,...随后存储在内存中,但不保证元素v[0].back()
和v[1].front()
。【参考方案2】:
正如其他答案所指出的,向量的内容保证是连续的(除了布尔的怪异)。
我想添加的注释是,如果您对向量执行插入或删除操作,这可能会导致向量重新分配其内存,那么您将导致所有保存的指针和迭代器无效。
【讨论】:
元素仍将存储在连续的内存块中,只是在不同的位置。这个问题专门针对连续性。 但是现有的指针和迭代器会失效。 好点。您应该将其放入您的答案中以阐明您的意思。 现在我知道为什么我的程序昨天出现了段错误,当时我在一个双循环中删除了某些元素:) 谢谢! @iaomw: 1.vector.push_back(3)
是一个插入,因此它使迭代器无效。 2. 我不希望 swap(vector[3], vector[4])
使迭代器无效,因为没有分配新内存,但我没有参考来备份它。 3. swap(vector_1, vector_2)
很有趣。在此之后我可能不会信任迭代器,但我不确定它们是否继续有效。【参考方案3】:
该标准确实保证vector
在内存中是连续的,并且&a[0]
可以传递给需要数组的C
函数。
这个规则的例外是vector<bool>
,它每个bool
只使用一个位,因此虽然它确实有连续的内存,但它不能用作bool*
(这被广泛认为是错误的优化和一个失误)。
顺便说一句,你为什么不使用迭代器?这就是他们的目的。
【讨论】:
> 顺便说一句,你为什么不使用迭代器?这就是他们的目的。也许他阅读了 Alexanrescu 关于该主题的新论文:boostcon.com/site-media/var/sphene/sphwiki/attachment/2009/05/… 谢谢你的链接,我会把它加入我的阅读清单(我尽量不要错过 Alexandresu 的文章) Mwahaha,这些天似乎每个人都在谈论那个演示文稿。看,关于它的讨论仍然很热:groups.google.com/group/comp.lang.c++.moderated/browse_thread/… 如果您仔细阅读,Alexandrescu 的文章并没有真正说“不要在 C++ 中使用迭代器”,而是说“检查 D”。他在那篇论文中描述的方法与吸收了功能遗产(List、Scheme、Haskell)的任何现有语言和框架惊人地相似,我严重怀疑另一种基于 C 的语法是否是更好的理想起点列表处理。去年的某个时候,我曾短暂地试图说服他将他的巨大才能转向改进像 C# 这样已经成熟的语言,但我担心不会成功! :)【参考方案4】:正如其他人已经说过的,vector
在内部使用了一个连续的对象数组。每当调用任何非 const 成员函数时,指向该数组的指针都应视为无效 IIRC。
但是,有一个例外!!
vector<bool>
有一个专门的实现,旨在节省空间,因此每个 bool 只使用一位。底层数组不是 bool 的连续数组,vector<bool>
上的数组算术不像 vector<T>
那样工作。
(我想这也可能适用于向量的任何特化,因为我们总是可以实现一个新的。但是,std::vector<bool>
是唯一的,错误的,标准特化,简单的指针运算不会工作。)
【讨论】:
不允许用户特化std::vector
,所有其他向量都需要使用连续存储。因此,std::vector<bool>
(幸运的是)是唯一一个奇怪的标准向量。 (我强烈认为这种专业化应该被弃用并被替换为例如具有相同功能的std::dynamic_bitset
。这不是一个糟糕的数据结构,它只是不是一个向量。)【参考方案5】:
我找到了这个帖子,因为我有一个用例,其中使用连续内存的向量是一个优势。
我正在学习如何在 OpenGL 中使用顶点缓冲区对象。我创建了一个包装类来包含缓冲区逻辑,所以我需要做的就是传递一个浮点数组和一些配置值来创建缓冲区。 我希望能够根据用户输入从函数生成缓冲区,因此在编译时长度是未知的。做这样的事情将是最简单的解决方案:
void generate(std::vector<float> v)
float f = generate_next_float();
v.push_back(f);
现在我可以将向量的浮点数作为数组传递给 OpenGL 的缓冲区相关函数。这也消除了使用 sizeof 来确定数组长度的需要。
这比分配一个巨大的数组来存储浮点数并希望我把它做得足够大,或者用连续存储创建我自己的动态数组要好得多。
【讨论】:
这个函数对我来说没有任何意义。您的意思是传递一个引用或指向v
而不是v
本身的指针?因为单独传递v
会导致在函数内部生成一个副本,该副本将在函数结束后不复存在。因此,您只是在函数结束时将某些内容推送到向量上以删除向量。【参考方案6】:
cplusplus.com:
向量容器被实现为动态数组;与常规数组一样,向量容器的元素存储在连续的存储位置,这意味着不仅可以使用迭代器访问它们的元素,还可以使用指向元素的常规指针的偏移量来访问它们的元素。
【讨论】:
【参考方案7】:是的,std::vector 的元素保证是连续的。
【讨论】:
以上是关于std::vector 元素是不是保证是连续的?的主要内容,如果未能解决你的问题,请参考以下文章
指向 std::vector 和 std::list 元素的指针