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

时间:2014-06-28 21:18:17

标签: c++

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 ++标准的任何编译器是否总能产生相同的输出?

2 个答案:

答案 0 :(得分:2)

您正在向std::uint32_t投射一个缓冲区,该缓冲区未必正确对齐此值。

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

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

因此,您可以执行memcpy并且您可以,技术上 - 只要有足够的字节s.length() >= sizeof(std::uint32_t)

然而,如果这种转换发生在普通代码中,那将是一种强烈的代码嗅觉,表明设计中出现了根本性的错误。


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

当我回答的时候,我不知道怎么回事。但简短的回答是,如果转换是以一种有效的方式执行的,例如使用memcpy,那么它取决于 endianness ,又名字节顺序实际上,整数中最重要或最不重要的部分是否放在最低地址。

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

答案 1 :(得分:2)

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

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

C ++ 11 5.2.10 / 7&#34;重新解释演员&#34;表示:

  

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

由于uint32_t通常会比char[]具有更严格的对齐要求,因此标准不会对行为作出任何承诺(因为上述内容仅涉及对齐要求的情况)满足)。严格来说,行为是不明确的。

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

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

该标准说明了如何在5.2.9 / 13&#34;静态演员&#34;

中使用static_cast和对象指针(除了类heirarchy中的指针之间的转换)
  

类型为“指向 cv1 void的指针”的prvalue可以转换为prvalue   类型为“指向 cv2 T的指针,”其中T是对象类型而 cv2 是   与 cv1 相同的cv资格,或更高的cv资格。该   空指针值被转换为空指针值   目的地类型。转换为对象的类型指针的值   “指向 cv void的指针”并返回,可能有不同的   cv-qualification,应具有原始值。

因此,就标准而言,您可以对结果指针执行的所有操作都将其强制转换为获取原始值。其他任何东西都是未定义的行为(实现可能会提供更好的保证)。

3.10 / 10&#34; Lvalues和rvalues&#34;允许通过charunsigned char类型访问对象。

但是,重申一下:标准并不保证&#34;任何符合c ++标准的编译器总是产生相同的输出&#34;对于您发布的示例。