ChatGPT解决这个技术问题 Extra ChatGPT

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

我的问题很简单: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:什么麻烦——你能详细说明一下吗?
我想他的意思是推送新值可能会触发“重新分配”,这会导致数组变得无效。
改变 values 的调用,特别是改变其大小的调用(例如,push_back()),可能会提示重新分配底层向量,从而使复制到 array 的指针无效。这与使用 vector::iterator 而不是指向向量的指针背后的原理相同。 :)
是的,我把 ``'s 放在值周围是为了表明我在谈论类本身,而不是其中包含的值。 :) 不幸的命名等等。我不认为在这个问题相关的一般情况下这真的是一个问题 - 为什么有人会抓住一个指向内存的指针,然后开始使用向量而不是使用指针?愚蠢。

d
dirkgently

这在 C++98 标准中被遗漏,但后来作为 TR 的一部分添加。即将发布的 C++0x 标准当然会将此作为要求。

来自 n2798(C++0x 草案):

23.2.6 类模板向量[vector] 1 向量是支持随机访问迭代器的序列容器。此外,它还支持(摊销)恒定时间的最后插入和擦除操作;在中间插入和擦除需要线性时间。存储管理是自动处理的,但可以给出提示以提高效率。向量的元素是连续存储的,这意味着如果 v 是一个向量,其中 T 是除 bool 之外的某种类型,那么对于所有 0 <= n < v,它都遵循恒等式 &v[n] == &v[0] + n 。尺寸()。


这在 ISO 14882,第 2 版中也有说明:第 23.2.4 节 [lib.vector]:“向量的元素是连续存储的,这意味着如果 v 是 vector 其中 T 是其他类型bool,那么对于所有 0 <= n < v.size(),它都遵循恒等式 &v[n] == &v[0] + n。"
所以 s,TR,TC, :) 实际上 C++03 也被称为 C++98-TC1 (技术勘误)从我读到
向量的向量呢? İnner 向量就在最后一组的内部向量之后?
@huseyin tugrul buyukisik 你知道答案了吗?我也想知道这是如何工作的
@huseyin tugrul buyukisik 这当然是真的,但是后续std::vector 的实例是连续的。例如:在std::vector<std::vector<int>> v 中,元素 v[0]v[1]、...随后存储在内存中,但不保证元素 v[0].back()v[1].front()
B
Bill Lynch

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

我想添加的评论是,如果您对向量执行插入或删除操作,这可能导致向量重新分配其内存,那么您将导致所有保存的指针和迭代器无效。


元素仍将存储在连续的内存块中,只是位于不同的位置。问题特别是关于连续性。
但是现有的指针和迭代器将失效。
好点子。您应该将其放入答案中以澄清您的意思。
现在我知道为什么我的程序昨天出现段错误,当我在一个双循环中循环它时删除某些元素:) 谢谢!
@iaomw:1. vector.push_back(3) 是一个插入,因此它使迭代器无效。 2. 我不希望 swap(vector[3], vector[4]) 使迭代器无效,因为没有分配新内存,但我没有引用来备份它。 3. swap(vector_1, vector_2) 很有趣。在此之后我可能不会信任迭代器,但我不确定它们是否继续有效。
M
Motti

实际上,该标准确实保证 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# 这样已经建立起来的语言,但我担心不会成功! :)
W
Wuggy

正如其他人已经说过的那样,vector 在内部使用了一个连续的对象数组。只要调用任何非常量成员函数 IIRC,指向该数组的指针都应被视为无效。

但是,有一个例外!!

vector<bool> 有一个专门用于节省空间的实现,因此每个 bool 只使用一位。底层数组不是一个连续的 bool 数组,并且 vector<bool> 上的数组算术不像 vector<T> 那样工作。

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


不允许用户专门化 std::vector,并且所有其他向量都需要使用连续存储。因此,std::vector<bool> 是(幸运的是)唯一奇怪的标准向量。 (我强烈认为应该弃用这种专业化,并用具有几乎相同功能的 std::dynamic_bitset 代替。这不是一个糟糕的数据结构,它只是不是一个向量。)
N
NobodyImportant

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

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

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

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

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


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

cplusplus.com:

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


B
Benoît

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