这已经困扰了我很长时间:如何将指针从任何内容转换为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
来完成类似于serialize
的任务,并且将指针与sizeof
一起用作整数数学,
但我猜这里也是UB。
即使他们是UB,编译器作者也通常会做一些合理的事情来确保一切正常。 我对此表示同意:要求这不是不合理的事情。
对于上述两个常见任务,我的问题确实是:
谢谢!
答案 0 :(得分:6)
您的serialize
实现的行为是不确定的,因为您违反了strict aliasing规则。简而言之,严格的别名规则表明,您不能通过指针或其他类型的引用来引用任何对象。但是,该规则有一个主要例外:可以通过指向char
,unsigned 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];
//}
}
在这里,buffer
不会将std::memcpy
强制转换为不兼容类型的指针,而是将指针转换为对象,以序列化为指向unsigned char
的指针。这样,就不会违反严格的别名规则,并且程序的行为仍保持良好定义。请注意,确切的表示形式仍未指定;因为这取决于您的CPU的耐久性。