【问题标题】:Is conversion between vectors defined behavior?向量之间的转换是否定义了行为?
【发布时间】:2019-01-21 18:30:06
【问题描述】:

为了在我的游戏中序列化组件,我需要能够访问各种向量中的数据,只要给定一个指针和向量的大小。

如果我只有一个指向向量的 void *,我想从向量中获取 data() 指针。我正在尝试从 std::vector<T> 转换为 std::vector<char> 以获取 data() 指针。我想知道下面的代码是否是定义的行为,并且在不同的情况下不会有任何不同。

#include <iostream>
#include <vector>

int main()
{
    std::vector<int> ints = { 0, 1, 2, 3, 4 };

    std::vector<char>* memory = reinterpret_cast<std::vector<char>*>(&ints);
    int *intArray = reinterpret_cast<int *>(memory->data());

    std::cout << intArray[0] << intArray[1] << intArray[2] << intArray[3] << intArray[4] << std::endl; //01234 Works on gcc and vc++
    std::getchar();
}

这似乎在这种孤立的情况下有效,但我不知道它是否会在序列化代码中产生错误或未定义的行为。

【问题讨论】:

  • 你为什么要通过std::vector&lt;char&gt;?你不知道原来的类型?
  • @Mat 关于向量唯一知道的是它的数据大小(以字节为单位)。
  • 我把它扔到“可能有效,但违反严格别名,因此不能保证结果”bin。有没有机会对此进行模板化,以免丢失类型?换句话说,您的示例可能过于简单,以至于任何人都无法指出所有潜在问题。
  • 为什么不只是char* memory = reinterpret_cast&lt;char*&gt;(ints.data())

标签: c++ visual-c++ language-lawyer stdvector undefined-behavior


【解决方案1】:

这是一个别名违规:

std::vector<char>* memory = reinterpret_cast<std::vector<char>*>(&ints);
int *intArray = reinterpret_cast<int *>(memory->data());

根据 [basic.life],在此处访问 memory-&gt;data() 具有未定义的行为。

解决此问题的方法是调用ints.data() 以获取指向底层连续数组的int* 指针。之后,您可以将其转换为 void*char*unsigned char*(或 C++17 中的 std::byte*)。

您可以从那里转换回int* 以再次访问这些元素。

【讨论】:

  • int* 转换为char*,然后返回int*然后 使用它不是别名违规。
  • @rustyx 但这不是这里发生的事情。我们将vector&lt;int&gt;* 转换为vector&lt;char&gt;*使用它,而无需先转换回vector&lt;int&gt;*。如果该行是reinterpret_cast&lt;int*&gt;(reinterpret_cast&lt;char*&gt;(ints.data())),那么这将是一个int* -&gt; char* -&gt; int* 转换序列。
  • 嗯?原始数组上的 动态类型int[] 并且保持不变。它可能与char* 有别名,但这并不会神奇地将其更改为char[]
  • @rustyx:请重新阅读 OPs 代码。他们将std::vector&lt;int&gt;* 转换为std::vector&lt;char&gt;*,然后通过结果指针调用std::vector&lt;char&gt;::data。这是一个严格的混叠违规,因此是 UB。他们最终将指针转换回int* 并不重要。事实是他们通过指针执行了间接寻址。请参阅 N4659 6.8:“允许通过此类指针进行间接访问,但生成的左值只能以有限的方式使用,如下所述。如果...,则程序具有未定义的行为”
  • @rustyx:你还不服气吗?
【解决方案2】:

我不认为是UB。

使用reinterpret_cast&lt;std::vector&lt;char&gt;*&gt;(&amp;ints),您将一个向量对象转换为另一个不同(实际上不兼容)类型的向量对象。然而,您并没有取消引用结果指针,并且 - 因为两个向量对象很可能具有相同的别名限制 - 转换就可以了。参见,例如this 在线 C++ 草案)。请注意,向量不会“就地”存储数据类型,而是会保存指向值的指针。

5.2.10 重新解释演员表

(7) 对象指针可以显式转换为对象指针 一种不同的类型。70 当类型为“指向 T1 的指针”的纯右值 v 是 转换为“指向 cv T2 的指针”类型,如果 T1 和 T2 都是标准布局,则结果为 static_cast(static_cast(v)) types ([basic.types]) 和 T2 的对齐要求是 no 比 T1 更严格,或者如果任一类型为 void。转换一个 “指向 T1 的指针”类型的纯右值到类型“指向 T2 的指针”(其中 T1 和 T2 是对象类型,其中 T2 的对齐要求是 不比 T1 更严格)并返回其原始类型产生 原始指针值。任何其他此类指针的结果 转换未指定。

因此,来回转换矢量对象应该在这里以定义的方式工作。

其次,您将最初指向(并别名为)int 的指针“返回”到其原始类型 int。所以别名显然没有被违反。

我在这里看不到任何 UB(除非向量对象具有比向量对象更严格的别名规则,这很可能不是这种情况)。

【讨论】:

  • @AndyG:是的。我监督矢量对象没有被退回。
  • 看起来你现在可以:-)
  • @AndyG: 并将在几分钟内查看 :-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-02-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多