【问题标题】:Is accessing the raw pointer after std::vector::reserve safe?在 std::vector::reserve 之后访问原始指针是否安全?
【发布时间】:2011-11-22 14:04:29
【问题描述】:

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

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

我意识到这很丑 - 我只想知道它在技术上是否安全,而不是“漂亮”。我猜它的唯一用途可能是忽略存储在给定索引之外的值。

注意! How can I get the address of the buffer allocated by vector::reserve()? 涵盖了相同的主题,但我更感兴趣的是这是否安全以及这样做是否存在陷阱。

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

【问题讨论】:

  • 好吧,这太丑了,太疼了。你为什么这样做?如果你真的需要的话,你不能简单地使用一个数组吗?在这个例子中 100 是固定的,所以你可以在堆栈上使用一个数组,甚至不需要 delete[]ing...
  • “分段错误”是特定于平台的事件。 C++ 语言没有描述它是什么。语言只说明是否定义了某些东西,如果定义了,做什么。
  • 我对这个问题投了反对票,不是因为我认为这是一个不好的问题,而是因为你没有花足够的时间来确保你问的是你想要问的(原始代码和当前版本中的代码完全不同)。 -2 代表点并不多,但应该提醒你以后要小心一点,因为当你要求其他人花时间回答试图帮助时,如果你稍后重新提出问题,那就是浪费时间开。
  • 如果只考虑这里的 3 行代码,我相信我在 stackoverflow.com/questions/19064318 中的回答表明代码定义明确。

标签: c++ stl vector


【解决方案1】:

不,不安全。

reserve() 之后,向量保证在到达capacity() 之前不会重新分配存储空间。

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

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

【讨论】:

  • 因指出 为什么 这是一个坏主意而得到支持:保留的内存是 UB 并且不能通过向量“正式”使用,直到它被 resized 或 @ 987654326@/emplaced 到。
【解决方案2】:

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

【讨论】:

  • 更新了我的问题以使用 memset 而不是 `memcpy(我的示例不正确)。
【解决方案3】:

首先请注意,您的 memset 会将 0x123 截断为单个字节并写入,而不是写入四字节模式。

那么,不要那样做,直接使用容器:std::vector&lt;int&gt; vec(100, whatever_value_you_want);

但是要回答这个问题,如果编译器不将分配的空间用于任何事情,它可能似乎专门针对 POD 类型工作。当然,如果有人调用resizeinsertpush_back 等,它会吹走你已经写入内存的任何内容,并且向量的大小也会出错。没有理由编写这样的代码。

【讨论】:

    【解决方案4】:

    我认为接受的答案不正确。

    如果vector的内容是POD类型(例如问题中的int,或者char等),那么reserve()ed内存是行为良好的对象,标准保证没有应该对size()capacity()之间的存储做任何意外。

    在我的回答https://stackoverflow.com/a/69141237/2791230 中给出了我的论点,并引用了标准。在这里,我特别说明了为什么接受的答案中的问题不能发生。

    “也许 [reserve()ed memory] ​​可以用于一些内部数据。”不,不可能。考虑insert() 元素到向量的末尾,直到reserve()ed 内存被填满。该标准确保除了明显的填充(及其结果,例如更改size())之外没有任何副作用。因此,如果某些内部数据被insert() 覆盖(这当然是副作用),将违反标准。

    “也许地址空间只是保留而不映射到实际 RAM?”这显然是不可能的,因为reserve()ed 内存中已经存在对象,详情请参阅参考答案。

    总的来说,我认为人们已经准备好声称某物是 UB。除非参考标准明确指出某物是 UB,否则所有声称“它是 UB”都应表示“我不知道它已定义,所以我认为它是 UB”,因为 UB 的意思是“它在整体标准。”

    【讨论】:

    • 您在倒数第二段中的声明不正确。现代操作系统在进行动态内存分配时通常使用惰性内存分配 - 并且仅在以某种非平凡方式使用内存时才实际执行分配(例如,它被覆盖,调用构造函数进行初始化等)。除了索引为0.size()-1 的元素之外,C++ 标准也不要求任何对象“活”在由向量管理的内存中(保留或其他)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-09-22
    • 1970-01-01
    • 2013-06-18
    • 1970-01-01
    • 2015-01-04
    相关资源
    最近更新 更多