为什么mapped_file::data返回char*而不是void*

Why mapped_file::data returns char* instead of void*

本文关键字:void char 返回 mapped file data 为什么      更新时间:2023-10-16

或者更好的模板<T*> ?

如果内存映射文件包含一个32位整数序列,如果data()返回一个void*,我们可以直接静态转换为std::uint32_t

为什么boost作者选择返回一个char* ?

编辑:正如所指出的,如果可移植性是一个问题,则需要翻译。但是,说一个文件(或在这种情况下的一块内存)是一个字节流,而不是比特流,或IEEE754双精度,或复杂的数据结构,在我看来,这是一个非常宽泛的说法,需要更多的解释。

即使必须处理端序,能够直接映射到be_uint32_t的向量(并在这里实现)将使代码更具可读性:

struct be_uint32_t {
  std::uint32_t raw;
  operator std::uint32_t() { return ntohl(raw); }
};
static_assert(sizeof(be_uint32_t)==4, "POD failed");

是否允许/建议转换为be_uint32_t* ?为什么,或者为什么不?

应该使用哪种类型的强制转换?

EDIT2:既然要讨论一个细化器的内存模型是由位、字节还是词组成的,似乎很难切入正题,我将重新措辞,给出一个例子:

#include <cstdint>
#include <memory>
#include <vector>
#include <iostream>
#include <boost/iostreams/device/mapped_file.hpp>
struct entry {
  std::uint32_t a;
  std::uint64_t b;
} __attribute__((packed)); /* compiler specific, but supported 
                              in other ways by all major compilers */
static_assert(sizeof(entry) == 12, "entry: Struct size mismatch");
static_assert(offsetof(entry, a) == 0, "entry: Invalid offset for a");
static_assert(offsetof(entry, b) == 4, "entry: Invalid offset for b");
int main(void) {
  boost::iostreams::mapped_file_source mmap("map");
  assert(mmap.is_open());
  const entry* data_begin = reinterpret_cast<const entry*>(mmap.data());
  const entry* data_end = data_begin + mmap.size()/sizeof(entry);
  for(const entry* ii=data_begin; ii!=data_end; ++ii)
    std::cout << std::hex << ii->a << " " << ii->b << std::endl;
  return 0;
}

如果map文件包含正确顺序的预期位,是否有其他原因避免使用reinterpret_cast来使用我的虚拟内存而不首先复制它?

如果没有,为什么要通过返回一个类型化指针来强制用户进行reinterpret_cast ?

请回答所有问题以获得加分

如果内存映射文件包含一个32位整数序列,如果data()返回一个void*,我们可以直接静态强制转换为std::uint32_t

不,不完全是。您仍然需要考虑(如果没有别的)端序。这种"一步转换"的想法会让你产生一种错误的安全感。您忘记了文件中的字节和您想要进入程序的32位整数之间的整个转换层。即使该翻译碰巧在您当前的系统上没有操作,对于给定的文件,它仍然是一个翻译步骤。

最好得到一个字节数组(字面上是char*指向的!),然后你知道你必须做一些思考来确保你的指针转换是有效的,并且你正在执行任何其他需要的工作

char*表示原始字节数组,这与mapped_file::data在大多数情况下是相同的。

void*可能会产生误导,因为它提供的关于所包含类型的信息较少,并且需要更多的设置才能与char*一起工作-我们知道文件内容是一些字节,char*表示。

模板返回类型需要在库中执行到该类型的转换,而在调用方进行转换更有意义(因为库仅提供原始文件内容的接口,并且调用方明确知道这些内容是什么)。

返回char *似乎只是boost::iostreams实现的一个(特殊的)设计决策。

其他api,例如boost进程间返回void*

根据sehe的观察,UNIX的mmap规范(和malloc)也使用void*

它在某种程度上是void*或char*的复制,用于通用缓冲区表示?

作为注意事项,Lightness在另一个答案中提到的翻译层可能需要从一个体系结构写入内存并在另一个体系结构上读取内存。端序性很容易通过转换类型解决,但是对齐也需要考虑。

关于静态强制转换:http://en.cppreference.com/w/cpp/language/static_cast提及:

指向void(可能是cv限定的)的指针类型的右值可以是转换为指向任何类型的指针。如果为原指针的值满足目标类型的对齐要求,则结果指针值不变,否则未指定。将任何指针转换为指向void的指针,再转换回指向原始(或更符合cv条件的)类型保留其原始值。

因此,如果要被内存映射的文件是在不同的体系结构上以不同的对齐方式创建的,则加载可能会失败(例如使用SIGBUS),这取决于体系结构和操作系统。