C ++:在这些情况下,reinterpret_cast是最佳选择吗?

Ter*_*sao 3 c++ reinterpret-cast pointer-conversion

这困扰了我很长时间:如何进行从任何东西char *到将二进制文件转储到磁盘的指针转换。

在C语言中,您甚至无需考虑。

double d = 3.14;
char *cp = (char *)&d;

// do what u would do to dump to disk
Run Code Online (Sandbox Code Playgroud)

但是,在C ++中,每个人都说C-cast不被接受,我一直在这样做:

double d = 3.14;
auto cp = reinterpret_cast<char *>(&d);
Run Code Online (Sandbox Code Playgroud)

现在这是从cppreference复制的,所以我认为这是正确的方法。

但是,我从多个消息来源读到这是UB。(例如,这个)所以我不禁要问是否有任何“ DB”方式(根据那篇文章,没有)。

我经常遇到的另一种情况是实现这样的API:

void serialize(void *buffer);
Run Code Online (Sandbox Code Playgroud)

您将在其中将很多东西转储到此缓冲区。现在,我一直在这样做:

void serialize(void *buffer) {
    int intToDump;
    float floatToDump;

    int *ip = reinterpret_cast<int *>(buffer);
    ip[0] = intToDump;

    float *fp = reinterpret_cast<float *>(&ip[1]);
    fp[0] = floatToDump;
}
Run Code Online (Sandbox Code Playgroud)

好吧,我想这也是UB。

现在,真的没有“ DB”方法可以完成这两项任务吗?我见过有人uintptr_t用来完成类似于sth的serialize任务,将指针与一起作为整数数学sizeof,但我猜这里也是UB。

即使他们是UB,编译器作者也通常会做一些合理的事情来确保一切正常。我对此表示同意:要求这不是不合理的事情。

因此,对于上述两个常见任务,我的问题确实是:

  1. 真的没有“ DB”方法可以满足最终的C ++怪胎吗?
  2. 除了我一直在做的以外,还有其他更好的方法来实现它们吗?

谢谢!

Mil*_*nek 6

您的serialize实现行为是不确定的,因为您违反了严格的别名规则。简而言之,严格的别名规则表明,您不能通过指针或其他类型的引用来引用任何对象。有一个主要例外该规则虽然:任何对象可经由指针被引用到charunsigned char或(因为C ++ 17) std::byte。请注意,此异常不适用于其他情况;一个char阵列可以不经由指针以外的类型来访问char

这意味着您可以serialize通过如下更改使其功能良好定义:

void serialize(char* buffer) {
    int intToDump = 42;
    float floatToDump = 3.14;

    std::memcpy(buffer, &intToDump, sizeof(intToDump));
    std::memcpy(buffer + sizeof(intToDump), &floatToDump, sizeof(floatToDump));

    // Or you could do byte-by-byte manual copy loops
    // i.e.
    //for (std::size_t i = 0; i < sizeof(intToDump); ++i, ++buffer) {
    //    *buffer = reinterpret_cast<char*>(&intToDump)[i];
    //}
    //for (std::size_t i = 0; i < sizeof(floatToDump); ++i, ++buffer) {
    //    *buffer = reinterpret_cast<char*>(&floatToDump)[i];
    //}
}
Run Code Online (Sandbox Code Playgroud)

在这里,不是强制转换buffer为不兼容类型的指针,而是std::memcpy强制转换为对象的指针以序列化为的指针unsigned char。这样,就不会违反严格的别名规则,并且程序的行为仍保持良好定义。请注意,确切的表示形式仍未指定;因为这将取决于您的CPU的耐久性。

  • @xtofl使用std :: copy,您需要从int *到char *的显式转换(std :: memcpy隐藏了),但是,我怀疑两者会编译为[exact相同的程序集](https://godbolt.org/z/sMDxAe) (2认同)