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 是向量 其中 T 是除 bool 之外的某种类型,那么它对于所有 0 so s,TR,TC, :) 根据我的阅读,实际上 C++03 也称为 C++98-TC1(技术勘误) 向量的向量呢? İnner 向量就在最后一组的内部向量之后? @huseyin tugrul buyukisik 你知道答案了吗?我也想知道这是如何工作的 @huseyin tugrul buyukisik 这当然是真的,但后续std::vector 的实例是连续的。例如:在std::vector&lt;std::vector&lt;int&gt;&gt; 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 在内存中是连续的,并且&amp;a[0] 可以传递给需要数组的C 函数。

这个规则的例外是vector&lt;bool&gt;,它每个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&lt;bool&gt; 有一个专门的实现,旨在节省空间,因此每个 bool 只使用一位。底层数组不是 bool 的连续数组,vector&lt;bool&gt; 上的数组算术不像 vector&lt;T&gt; 那样工作。

(我想这也可能适用于向量的任何特化,因为我们总是可以实现一个新的。但是,std::vector&lt;bool&gt; 是唯一的,错误的,标准特化,简单的指针运算不会工作。)

【讨论】:

不允许用户特化std::vector,所有其他向量都需要使用连续存储。因此,std::vector&lt;bool&gt;(幸运的是)是唯一一个奇怪的标准向量。 (我强烈认为这种专业化应该被弃用并被替换为例如具有相同功能的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::vector 和 std::list 元素的指针

std::vector 在清除或删除 POD 时的行为是不是不同

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

std::vector

指向std :: vector和std :: list元素的指针