有一个指向保留向量元素的指针是否合法?

时间:2014-12-20 15:42:31

标签: c++ pointers vector language-lawyer undefined-behavior

我很好奇这种事情是否合法:

std::vector<some_class_type> vec;
vec.reserve(10);
some_class_type* ptr = vec.data() + 3; // that object doesn't exist yet

请注意,我没有尝试访问指向的值。

这是标准对data()所说的内容,但我不确定它是否相关:

  

返回:指针使[data(),data() + size())有效   范围。对于非空向量data() == &front()

5 个答案:

答案 0 :(得分:3)

您提供的示例未显示任何立即未定义的行为。根据标准,因为您保留的元素数量大于向量的当前容量,将重新分配。由于分配发生在调用reserve的位置,data()返回的指针本身有效。

23.3.6.3/2(强调我的)

  

效果:一种指令,通知向量计划的大小更改,以便它可以相应地管理存储分配。在reserve()之后,如果重新分配,capacity()大于或等于reserve的参数;并且等于capacity()的先前值。 当且仅当当前容量小于reserve()的参数时,才会发生重新分配。如果除了非CopyInsertable类型的move构造函数之外抛出异常,则没有效果。

但是,如果在添加指针位于data() + size()之外的足够元素之前尝试取消引用指针,或者如果添加的时间超过capacity(),则会发生未定义的行为。

答案 1 :(得分:1)

  1. data 必须返回一个指向有效范围 ([vector.data]) 的指针。指向该范围的指针,包括指向该范围最后一个元素的指针,在下一次重新分配之前必须保持稳定。当 size() 为零时,data() 指向空范围的末尾,这是一个完全有效的要保留的指针(但当然不能取消引用)。
  2. 由于 data 必须在接下来的 10 次插入中保持稳定,因此我们可以假设获得了一个大小和对齐合适的存储区域,用于在其中放置 10 个元素的数组,并且 data 指向那个地区的开始。也就是说,实现必须调用一个分配函数并将内部数据指针设置为它的返回值。 (标准中没有直接指示这一定是真的,但我无法想象它可能是假的情况。我假设标准要求的重新分配 ([vector.capacity]) 实际上调用了一个分配函数,例如作为 ::operator newstd::malloc,否则称此操作为“重新分配”将相当可疑。无论如何,似乎无法避免在任何当前架构上进行此类分配)。
  3. 任何此类存储区域实际上都包含一个适当大小 ([intro.object]) 的实时数组(尽管不一定是实时数组元素,具体取决于元素类型)。此类数组中的指针算术是有效的,即使解引用结果指针不一定有效。

答案 2 :(得分:0)

在STL的大多数实现中,空向量的reserve将触发重新分配并确保您指向的数据被拥有/管理。

调整向量大小时,数据的位置(data()返回的指针的值)可能会发生变化。持有一个指针本身当然是合法的,取消引用它以进行读取,而未初始化当然是未定义的,如果你能保证,在初始化后取消它只是 合法你的矢量没有调整大小,因此你分配的范围仍然在同一个地方。

增加指向malloc&#39; d的数据的指针很好。在此示例中,您执行指针运算以保存指向您知道已由std::vector分配的数据的指针。无论指针指向的元素是否曾被初始化,调整大小操作都会出现问题,因为它可能会释放您指向的内存。

答案 3 :(得分:-1)

绝对:您的指针不能被认为是有效的。在这里证明它是UB:

标准说明了reserve()具有以下效果的capactity(23.3.6.3):

  

指示通知计划更改大小的向量,因此   它可以相应地管理存储分配。后   reserve(),capacity()大于或等于reserve的参数   如果重新分配;并且等于容量的先前值()   除此以外。此时重新分配发生,当且仅当   当前容量小于reserve()的参数。

因此,该标准保证如果分配了某些东西且容量不足,则必须在此时进行重新分配。但仅此而已。

这个措辞让实现以其他方式管理空向量。例如,可以完全想象一个实现可能不会为刚刚创建的空向量分配内存,并且仅在添加第一个元素时分配所需的容量(“延迟分配”策略)。

在这种情况下,您的示例将导致指向无效地址的指针。

重要编辑: 有些人可能认为“ if if only only if ”子句将确保必须在示例中进行分配,因为新容量将大于初始容量。但是,该标准 没有对矢量 的初始容量做出任何声明。使用面向集团的懒惰分配策略的实现(即通过最小的集合管理容量,比如10个项目)将符合标准并导致您的示例指向无效地址,如上所述。

答案 4 :(得分:-2)

当然,这是合法的。 您提到的引用无关紧要,因为 size 不等于 reserve 提供的“保留空间”。 您也可以在 vec.data()+3 之前初始化 vec[0],但不会更新向量的“大小”变量。

因此,尽管使用 vector 非常不受欢迎,但 vector 只不过是动态分配数组的一个瘦包装器,以这种方式滥用 vector 并不违法。

经验法则:一旦您使用 vector::data() 函数,您就做错了事。