保证用于序列化目的的reinterpret_cast输出

guarantee of reinterpret_cast output for serialization purpose

本文关键字:reinterpret cast 输出 用于 序列化      更新时间:2023-10-16
int main()
{
    char buffer[5] = { 0 };
    buffer[0] = 23;
    std::string s(&buffer[0], 4);
    std::uint32_t nb = *reinterpret_cast<const std::uint32_t*>(s.data());
    return 0;
}

对于此程序,reinterpret_cast的输出实现是否取决于?还是任何符合 c++ 标准的编译器总是产生相同的输出?

对于您的示例代码,如果您正在寻找"任何符合 c++ 标准的编译器始终产生相同的输出"的东西,答案是没有这样的保证。

几个简单的例子:对齐问题(如几条评论所述(和字节序差异。

C++11 5.2.10/7 "重新诠释演员表"说:

对象指针可以显式转换为 不同的类型。当类型为"指向T1的指针"的 prvalue v 转换为类型"指向 CV T2 的指针",结果为 static_cast<cv T2*>(static_cast<cv void*>(v)) T1T2是否都是标准布局 类型(3.9(和T2的对齐要求不比 T1 的那些,或者如果任一类型是 void 。转换类型的 prvalue "指向T1的指针"到"指向T2的指针"类型(其中T1T2位于 对象类型以及T2的对齐要求为否 比 T1 的更严格,并返回到其原始类型,产生 原始指针值。任何其他此类指针的结果 未指定转换。

由于uint32_t通常比char[]有更严格的对齐要求,因此该标准不对行为做出任何承诺(因为上面只讨论了满足对齐要求的情况(。所以严格来说,行为是不确定的。

现在,假设您只对满足对齐要求的平台感兴趣(即,uint32_t可以在任何地址上对齐,与char相同(。然后,涉及重新解释强制转换的表达式等效于(请注意,您还必须从从std::string::data()返回的const char*中丢弃const(:

std::uint32_t nb = *(static_cast<std::uint32_t*>(static_cast<void*>(const_cast<char*>(s.data()))));

该标准在 5.2.9/13 "静态强制转换"中对对象指针使用static_cast(类层次结构中的指针之间的转换除外(是这样说的:

类型为">指向 cv1 void 的指针"的 prvalue 可以转换为 prvalue 类型"指向 CV2 T 的指针",其中 T 是对象类型,CV2 是 简历资格与简历1相同,或比CV1更高。这 空指针值将转换为 目标类型。指向对象的指针类型的值转换为 "指向 cv void 的指针"和返回,可能具有不同的 简历资格,应有其原始价值。

因此,就

标准而言,您对生成的指针所能做的就是将其转换回去以获取原始值。其他任何东西都是未定义的行为(实现可能会提供更好的保证(。

3.10/10 "左值和右值"也允许通过charunsigned char类型访问对象。

但是,重申一下:该标准不保证"任何符合c ++标准的编译器始终产生相同的输出"对于您发布的示例。

您正在强制std::uint32_t一个缓冲区,该缓冲区不一定与此类值正确对齐。

这可能会爆炸和/或效率极低。

无符号整数类型意味着值表示位的任何位模式都是可以的,并且在内置类型的PC平台上,除了值表示位之外没有其他位;特别是没有陷阱位或捕获总位模式。

因此,你可以做一个memcpy,从技术上讲,你会没事的——只要有足够的字节,s.length() >= sizeof(std::uint32_t).

但是,如果这种转换发生在普通代码中,则会产生强烈的代码气味,表明设计中存在根本性错误。


附录,关于"或者一个尊重 c++ 标准的编译器将始终产生相同的输出"。

当我回答时,我不知何故没有看到这一点。但简短的回答是,如果转换以有效的方式执行,例如使用 memcpy ,那么它取决于字节,在实践中是将整数的最有效部分还是最不重要的部分放在最低地址。

实际上,您可以使用从网络字节顺序转换为的面向网络的函数。只需假设序列化数据的网络字节顺序。查看ntohl等人(这些不是C++标准库的一部分,但通常可用(。