【问题标题】:Is reintrepret_cast to char* well defined when the char array is modified?修改 char 数组时是否明确定义了 reintrepret_cast to char*?
【发布时间】:2020-12-16 12:59:32
【问题描述】:

从套接字接收预期消息时考虑以下示例:

struct myData {
    uint8_t type;
    int value;
}

myData readFromSocket(int socketFD) {
    myData data{};
    ssize_t bytes = recv(socketFD, reinterpret_cast<char*>(&data), sizeof(myData), 0);
    if(bytes == sizeof(myData))
        return data;
    return myData{};
}

在这个例子中,我不清楚行为是否定义良好。

根据reintrpret_cast on cppreference.comexamination 的行为已得到很好的定义,因为 char 的对齐方式不如 myData 严格,并且因为强制转换专门针对 char 指针。我不清楚检查是否只针对读取,或者是否包括对转换指针的写入。

5) 解释下:

任何对象指针类型 T1* 都可以转换为另一个对象指针类型 cv T2*。这完全等价于 static_cast(static_cast(expression)) (这意味着如果 T2 的对齐要求不比 T1 更严格,则指针的值不会改变并且结果指针的转换返回其原始类型会产生原始值)。在任何情况下,只有在类型别名规则允许的情况下,才能安全地取消引用结果指针(见下文)

以及类型别名的第三点:

AliasedType 是 std::byte (C++17 起)、char 或 unsigned char:这允许将任何对象的对象表示检查为字节数组。

我已经测试了与上述类似的代码,没有任何问题,但是由于这一切都归结为编译器做了哪些优化,我发现很难给出一个可能失败的确切示例。

This article 提到向另一个方向进行转换,即从char*myData 会受到未定义行为的影响,并建议使用memcpy()。我的假设是,由于类型别名规则没有涵盖强制转换,因此得出了这个结论。

但是,this mail thread 怀疑 memcpy(),根据标准,是否应该提供该保证(见下面的引用)并且没有阅读标准我倾向于同意,因为看起来相同的演员表已经完成memcpy() 至于recv()

在 C++ 社区中,目前的想法是 memcpy 允许键入双关语,但 IIRC C++ 标准实际上甚至不清楚为什么会这样,在其当前的写作中可能实际上没有方式。

无论如何,如果有人对此有所了解并能提供一些启示,我将不胜感激。我对这件事的兴趣更多的是学术而不是实际。我已经用 c++17 标记了它,因为这就是我正在从事的工作,欢迎对其他标准提出见解。

【问题讨论】:

  • 您的示例不完整,看起来您缺少(&amp;data)
  • 即使它没有很好地定义,它也是一个很常见的习惯用法,任何破坏它的编译器都会被回避。
  • 原则上,允许memcpy 覆盖myData 这样的标准布局类,recv 本质上就是这样做的。但这假设数据源最终是同一程序中的另一个myData。其他任何东西都假定myData 的表示。在这里,recv 可能正在从另一个程序获取数据流。考虑到字节顺序以及不同的编译器或配置如何改变myData 在内存中的布局方式,那么它可能不起作用。问题是该标准并没有真正考虑到进程间通信。
  • @Caleth 我认为这不适用,因为data 的生命周期已经由myData data{}; 明确启动。如果他们试图将指向char[] 的指针转换为myData *
  • @Caleth 假设您的意思是通过套接字接收到的chars,那么如果有问题,那是套接字实现的错误,与此问题无关。提供的示例没有charchar 数组。

标签: c++ pointers casting c++17 language-lawyer


【解决方案1】:

C++ 标准在[basic.lval]/p8 中指定别名规则:

如果程序尝试通过以下类型之一以外的左值访问对象的存储值,则行为未定义:
. . .
charunsigned charstd​::​byte 类型。

术语访问[defns.access]中指定:

⟨执行时动作⟩读取或修改对象的值

除了没有其他规则表明别名是单向的。

所以我们可以得出结论,别名是双向的,代码是可以的。

但是请注意,如果被覆盖的对象包含const 成员,那么你应该在覆盖后std::launder 它,否则允许编译器假定const 成员永远不会改变。

【讨论】:

  • 谢谢,这就是我要找的。​​span>
猜你喜欢
  • 2015-08-15
  • 1970-01-01
  • 2021-04-03
  • 1970-01-01
  • 1970-01-01
  • 2015-06-22
  • 1970-01-01
  • 2016-04-13
  • 1970-01-01
相关资源
最近更新 更多