其他分享
首页 > 其他分享> > C:reinterpret_cast是这些场景中的最佳选择吗?

C:reinterpret_cast是这些场景中的最佳选择吗?

作者:互联网

这已经困扰了我很长时间:如何将指针从任何东西转换为char *以将二进制转储到磁盘.

在C中,你甚至没有考虑过它.

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

// do what u would do to dump to disk

然而,在C中,每个人都说C-cast是不受欢迎的,我一直这样做:

double d = 3.14;
auto cp = reinterpret_cast<char *>(&d);

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

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

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

void serialize(void *buffer);

你要把很多东西都转储到这个缓冲区的地方.现在,我一直这样做:

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;
}

好吧,我想这也是UB.

现在,真的没有“DB”方法可以完成这些任务吗?
我见过有人使用uintptr_t来完成类似于序列化任务的指针作为整数数学以及sizeof,
但我猜这里也是UB.

即使它们是UB,编译器编写者通常会做出理性的事情以确保一切正常.
我对此很满意:要求它并不是一件不合理的事情.

所以我的问题确实是,对于上面提到的两个常见任务:

>真正没有“DB”方式来完成它们以满足最终的C怪胎吗?
>除了我一直在做的事情之外,还有更好的方法来完成它们吗?

谢谢!

解决方法:

您的序列化实现的行为未定义,因为您违反了strict aliasing规则.简而言之,严格的别名规则是指您不能通过指针引用任何对象或引用其他类型.但是该规则有一个主要的例外:任何对象都可以通过指向char,unsigned char或(自C 17)std :: byte的指针引用.请注意,此异常不适用于其他方式;可能无法通过指向char以外的类型的指针访问char数组.

这意味着您可以通过更改它来使序列化函数定义良好:

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];
    //}
}

这里,std :: memcpy不是将缓冲区转换为指向不兼容类型的指针,而是将指向该对象的指针强制转换为指向unsigned char的指针.这样做时,不会违反严格的别名规则,并且程序的行为仍然是明确定义的.请注意,确切的表示仍未指定;因为它取决于你的CPU的endianess.

标签:reinterpret-cast,c,pointer-conversion
来源: https://codeday.me/bug/20190823/1696913.html