修改 char 数组时,reintrepret_cast to char* 是否定义良好?

Kri*_*son 5 c++ pointers casting language-lawyer c++17

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

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{};
}
Run Code Online (Sandbox Code Playgroud)

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

根据cppreference.com 上的reintrpret_cast,该行为已明确定义以供检查,因为 char 的对齐不如 myData 严格,并且因为强制转换专门针对 char 指针。我不清楚检查是否仅限于读取或是否包括对强制转换指针的写入。

5)解释下:

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

以及类型别名的第三点:

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

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

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

然而,这个邮件线程令人怀疑memcpy(),根据标准,应该给出这样的保证(参见下面的引用),并且在没有阅读标准的情况下,我倾向于同意,因为它看起来像对 进行了相同的memcpy()转换recv()

在 C++ 社区中,当前的想法是 memcpy 允许输入双关语,但 IIRC C++ 标准实际上甚至不清楚为什么会出现这种情况,并且在其当前的编写中可能实际上没有办法。

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

rus*_*tyx 1

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

\n
\n

如果程序尝试通过以下类型之一以外的泛左值访问对象的存储值,则行为未定义:
\n。。。
\n\xe2\x80\x94 一个char, unsigned char, 或std\xe2\x80\x8b::\xe2\x80\x8bbyte类型。

\n
\n

术语access在[defns.access]中指定:

\n
\n

\xe2\x9f\xa8execution-time action\xe2\x9f\xa9 读取或修改对象的值

\n
\n

除此之外,没有其他规则表明别名是单向工作的。

\n

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

\n

但请注意,如果被覆盖的对象包含const成员,那么您应该std::launder在覆盖后添加成员,否则编译器可以假设const成员永远不会改变。

\n