std::vector::reserve后访问原始指针安全吗?

8

这有点牵强,但是下面的代码是否“安全”(即保证不会导致分段错误):

std::vector<int> vec(1); // Ensures that &vec[0] is valid
vec.reserve(100);
memset(&vec[0], 0x123, sizeof(int)*100); // Safe?

我知道这很丑陋-我只想知道它在技术上是否安全,不需要"漂亮"。 我猜它唯一的用途可能是忽略存储在给定索引之后的值。

注意!如何获取vector::reserve()分配的缓冲区地址?涵盖了相同的主题,但我更关心这是否安全以及在执行此操作时是否存在任何陷阱。

编辑:原始代码有误,已将原始 memcpy 替换为 memset


1
好的,这太丑陋了,令人痛苦。你为什么要这样做?如果你真的必须这样做,难道不能简单地使用一个数组吗?在这个例子中,100是固定的,所以你甚至可以在堆栈上使用一个数组,而无需进行delete[]... - Francesco
2
"Segmentation fault" 是一个特定于平台的事件。C++语言并没有描述它是什么。该语言只说明某些东西是否被定义,如果被定义了,则执行相应操作。 - Rob Kennedy
1
我已经将这个问题投下反对票,不是因为我认为这是一个不好的问题,而是因为您没有花足够的时间确保您所问的是否就是您想问的(原始代码和当前版本中的代码非常不同)。 -2分并不算多,但应该提醒您在未来要稍微注意一些,因为当您询问别人时,他们会花时间回答并试图帮助,如果您以后重新组织问题,那么浪费的就是那些时间。 - David Rodríguez - dribeas
如果仅考虑这里的三行代码,我相信我的回答在 https://dev59.com/g-o6XIcBkEYKwwoYLhTO 中表明了代码是良好定义的。 - wpzdm
4个回答

18

不,这是不安全的。

reserve()之后,vector保证不会重新分配存储空间,直到达到capacity()

然而,标准并没有说明在size()capacity()之间,vector实现可以对存储做什么。也许它可以用于一些内部数据 - 谁知道呢?也许地址空间只是保留下来而没有映射到实际的RAM上?

访问[0..size)之外的元素是未定义行为。可能有一些硬件检查。


感谢指出这是一个坏主意的_upped:保留的内存是未定义的,直到它被resize或者push/emplace使用后,才能被向量“正式”访问。 - underscore_d

2

向量的重新分配会使得现有指针、引用等无效。预留空间可能会触发重新分配 (参见23.3.6.2, [vector.capacity]),但是您正在获取最终重新分配后的第一个元素的地址(在这种情况下可能根本不会发生重新分配,但这并不重要)。所以我认为代码没有任何问题。


我更新了我的问题,使用memset而不是`memcpy(我的示例是错误的)。 - larsmoa

2

请注意,您的memset将把0x123截断为一个字节,并写入该字节,而不是写入四个字节的模式。

因此,不要这样做,只需使用容器:std::vector<int> vec(100, whatever_value_you_want);

然而,回答这个问题可能会出现在POD类型上特别有效的情况,如果编译器没有为任何其他用途分配空间。当有人调用resizeinsertpush_back等函数时,它会重置内存中已经写入的内容,向量的大小也将不正确。但实际上没有理由写这样的代码。


1
我认为被接受的答案是不正确的。
如果 vector 的内容是 POD 类型(例如问题中的 intchar 等),那么 reserve() 的内存是良好行为的对象,标准确保在 size()capacity() 之间的存储空间上不会发生任何意外。
我的观点,在我的回答中通过引用和参考标准进行了阐述 https://dev59.com/g-o6XIcBkEYKwwoYLhTO#69141237。在这里,我特别展示了为什么被接受的答案中的担忧是不可能发生的。
“也许[reserve()的内存]可以用于一些内部数据。” 不行,不能这样做。考虑向向量的末尾插入元素,直到reserve()的内存被填满。标准确保除了明显的填充(及其结果,例如改变size())之外没有副作用。因此,如果一些内部数据被insert()覆盖(这当然是一个副作用),那么这将与标准相违背。
“也许地址空间仅仅是被保留而没有映射到实际的RAM?”这更明显是不可能的,因为已经有对象存在于reserve()的内存中,详见相关答案。
总的来说,我认为人们过于容易声称某些东西是未定义行为。除非有参考文献表明标准明确表示某些东西是未定义行为,否则所有声称“它是未定义行为”的说法都应该意味着“我不知道它是否被定义,所以我认为它是未定义行为”,因为未定义行为意味着“在整个标准中都没有定义”。

你在倒数第二段的声明是不正确的。现代操作系统在进行动态内存分配时通常使用惰性分配内存的方式 - 只有在某些非平凡的方式中实际使用内存时(例如,被覆盖,调用构造函数进行初始化等),才会进行分配。C++标准也不要求任何对象“生活”在由向量管理的内存中 - 保留或其他 - 除了索引为0.size()-1的元素。 - Peter

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接