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

现在这是从 cpp首选项复制的, 所以我认为这是正确的方法。

但是,我从多个来源读到这是UB。 (例如这个) 所以我不禁想知道是否有任何"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来完成类似于serialize将指针作为整数数学的任务以及sizeof, 但我在这里猜测它也是 UB。

即使它们是 UB,编译器编写者通常也会做一些理性的事情来确保一切正常。 我对此没意见:这不是一件不合理的事情。

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

  1. 真的没有"DB"方法来完成它们来满足最终的C++怪胎吗?
  2. 除了我一直在做的事情之外,还有什么更好的方法来完成它们吗?

谢谢!

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

在这里,std::memcpy不是将buffer强制转换为指向不兼容类型的指针,而是将指向对象的指针强制转换为指向unsigned char的指针。 这样做不会违反严格的别名规则,并且程序的行为仍然明确定义。 请注意,确切的表示形式仍未指定;因为这将取决于您的 CPU 的字节序。

相关内容

  • 没有找到相关文章

最新更新