是否保证std :: vector元素是连续的?


111

我的问题很简单: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'
    }
}

我知道,如果您values在该if区块内进行变异,就会遇到麻烦。不过,我不知道您问题的答案,因此我只想发表评论。:)
Greg D

@Greg:什么麻烦–您能详细说明一下吗?
鲁南嫩

我想他的意思是,推送新值可能会触发“重新分配”,这将导致数组变为无效。
马丁·科特

values发生变化的调用(尤其是更改其大小(例如push_back()))可能会导致重新分配底层向量,从而使复制到中的指针无效array。使用vector :: iterator而不是指向vector的指针是相同的原理。:)
Greg D

1
是的,我将``''值放在周围以试图弄清楚我是在谈论类本身,而不是其中包含的值。:)不幸的命名等等。在一般情况下,尽管这个问题是相关的,但我不认为这确实是一个问题-为什么有人会抓住指向内存的指针,然后开始使用向量进行处理而不是使用指针?傻
格雷格D

Answers:


118

适当的C ++ 98标准缺少此功能,但后来将其添加为TR的一部分。当然,即将到来的C ++ 0x标准将包含此要求。

从n2798(C ++ 0x草案)起:

23.2.6类模板向量[vector]

1向量是支持随机访问迭代器的序列容器。此外,它最后还支持(摊销)恒定时间插入和擦除操作;在中间插入和擦除需要线性时间。存储管理是自动处理的,尽管可以提供一些提示以提高效率。向量的元素是连续存储的,这意味着如果v是向量,其中T是非bool的某种类型,则它对所有0 <= n <v都服从&v [n] ==&v [0] + n 。尺寸()​​。


3
ISO 14882,第二版,第23.2.4节[lib.vector]中也对此进行了说明:“向量的元素是连续存储的,这意味着如果v是vector <T,Allocator>,其中T是除bool,那么对于所有0 <= n <v.size(),它都服从&v [n] ==&v [0] + n的标识。”
迈克·卡伦

4
所以s,TR,TC,:)实际上,根据我的阅读,C ++ 03也被称为C ++ 98-TC1(技术勘误)
Johannes Schaub – litb

2
向量的向量呢?İnner向量在最后一组的内部向量之后吗?
huseyin tugrul buyukisik 2016年

1
@huseyin tugrul buyukisik您是否已了解答案?我也想知道这是如何工作的
大卫·多里亚

1
@huseyin tugrul buyukisik当然是对的,但是随后的实例std::vector是连续的。例如:在std::vector<std::vector<int>> v元素v[0]v[1],,... ...随后存储在内存中,但不能保证元素v[0].back()v[1].front()
jarzec

27

正如其他答案所指出的那样,保证向量的内容是连续的(布尔的怪异除外)。

我要添加的注释是,如果对向量进行插入或删除,这可能导致向量重新分配其内存,那么您将导致所有保存的指针和迭代器失效。


1
元素仍将存储在连续的存储块中,只是位于不同的位置。问题特别是关于连续性。
迪马

2
但是现有的指针和迭代器将失效。
比尔·林奇

好点子。您应该将其放入答案中以阐明您的意思。
迪马

对我最有用的答案
CoffeDeveloper 2013年

现在我知道为什么昨天我的程序出现段错误了,当我在一个双循环中循环删除了某些元素时:)谢谢!
user2891462 2015年

9

该标准实际上确实保证了a vector在内存中是连续的,并且&a[0]可以传递给C需要数组的函数。

该规则的例外是vector<bool>,每个规则仅使用一个位,bool因此尽管它确实具有连续的内存,但不能用作bool*(这被广泛认为是错误的优化和错误)。

顺便说一句,为什么不使用迭代器?那就是他们的目的。


1
>顺便说一句,为什么不使用迭代器?那就是他们的目的。:也许他看了Alexanrescu的新纸的话题boostcon.com/site-media/var/sphene/sphwiki/attachment/2009/05/...
维迪奇特里富诺维奇

感谢您的链接,我将其添加到我的阅读列表中(我尽量不要错过Alexandresu的文章)
Motti

夸哈哈,这些天似乎每个人都在谈论那个演讲。您看,讨论仍然很热烈:groups.google.com/group/comp.lang.c
++.moderated/browse_thread/…

如果仔细阅读,Alexandrescu的文章不会真正说“不要在C ++中使用迭代器”,而是说“签出D”。他在该论文中描述的方法与吸收功能继承的任何现有语言和框架都非常相似(List,Scheme,Haskell),而且我严重怀疑另一种基于C的语法是否是更好地实现这一目标的理想起点列表处理。去年的某个时候,我曾短暂地试图说服他,将他的才华转化为改进像C#这样已经建立的语言,但是我担心没有成功!:)
Daniel Earwicker 2010年

6

正如其他人已经说过的那样,vector内部使用了连续的对象数组。每当将任何非常量成员函数称为IIRC时,应将该数组中的指针视为无效。

但是,有一个例外!

vector<bool>具有旨在节省空间的专门实现,因此每个布尔仅使用一位。底层数组不是bool的连续数组,并且对数组进行的算法vector<bool>无法正常工作vector<T>

(我想向量的任何特殊化都可能是正确的,因为我们总是可以实现一个新的向量。但是,这std::vector<bool>是唯一的,错误的,标准的特殊化,简单指针算法将无法对其进行工作。)


不允许用户进行专门化std::vector,并且要求所有其他媒介使用连续存储。因此,std::vector<bool>(很幸运)是唯一奇怪的标准向量。(我强烈认为不建议使用此专业化,而应使用std::dynamic_bitset功能大致相同的a代替。这不是一个坏的数据结构,它不是向量。)
Arne Vogel

3

我找到此线程是因为我有一个用例,其中使用连续内存的向量是一个优势。

我正在学习如何在OpenGL中使用顶点缓冲区对象。我创建了一个包装器类来包含缓冲区逻辑,所以我要做的就是传递一个浮点数数组和一些配置值来创建缓冲区。我希望能够根据用户输入从函数生成缓冲区,因此在编译时长度未知。做这样的事情将是最简单的解决方案:

void generate(std::vector<float> v)
{
  float f = generate_next_float();
  v.push_back(f);
}

现在,我可以将向量的浮点作为数组传递给OpenGL的与缓冲区相关的函数。这也消除了使用sizeof确定数组长度的需要。

这远比分配一个巨大的数组来存储浮点数并希望我做得足够大,或者使自己的动态数组具有连续存储要好得多。


2
这个功能对我来说没有任何意义。您的意思是传递引用或指针v而不是v本身?因为v单独传递会导致在函数内部创建一个副本,该副本在函数结束后将不复存在。因此,您将某些内容推到向量上只是为了在函数结束时删除向量。
johnbakers

1

cplusplus.com:

向量容器被实现为动态数组。与常规数组一样,向量容器将其元素存储在连续的存储位置中,这意味着它们的元素不仅可以使用迭代器进行访问,还可以使用常规指向元素的偏移量进行访问。


1

是的,保证std :: vector的元素是连续的。


对。我想我用太多的那些:)
伯努瓦
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.