【问题标题】:About std::basic_ostream::write关于 std::basic_ostream::write
【发布时间】:2013-06-09 07:08:23
【问题描述】:

我正在阅读 basic_ostream 对象的 write 方法,这是我在 cppreference 上找到的:

basic_ostream& write( const char_type* s, std::streamsize count );

表现为UnformattedOutputFunction。构造并检查哨兵对象后,输出字符数组中第一个元素由s 指向的连续位置的字符。字符被插入到输出序列中,直到出现以下情况之一:

  • 插入的正是count 字符
  • 插入输出序列失败(在这种情况下会调用setstate(badbit)

所以我知道它会将一大块字符从缓冲区写入流中。而字符数是count指定的字节数。但有几件事我不确定。这些是我的问题:

  • 我应该只在我想指定多少字节写入流时才使用write?因为通常当您打印char 数组时,它会打印整个数组,直到到达空字节,但是当您使用write 时,您可以指定要写入多少个字符。

    char greeting[] = "Hello World";
    
    std::cout    << greeting;     // prints the entire string
    std::cout.write(greeting, 5); // prints "Hello"
    

    但也许我对这个有误解。

  • 我经常在使用write的代码示例中看到这一点:

    stream.write(reinterpret_cast<char*>(buffer), sizeof(buffer));
    

    为什么要使用reinterpret_castchar*?在写入流时,我什么时候应该知道做这样的事情?

如果有人能帮助我解决这两个问题,将不胜感激。

【问题讨论】:

    标签: c++ file stream buffer


    【解决方案1】:

    •仅当我想指定要写入流的字节数时才应该使用 write 吗?

    是的 - 当您希望按顺序写入流的内存中连续排列特定数量的数据字节时,您应该使用write。但有时您可能需要特定数量的字节并需要以其他方式获取它们,例如将double 的 ASCII 表示格式化为具有特定宽度和精度。

    其他时候你可能会使用&gt;&gt;,但这必须是用户为非内置类型定义的,并且当它被定义时——通常是为了更好,但对你的目的可能更糟——它会打印类设计者选择的任何内容,包括通过指针或引用从对象链接的潜在数据和感兴趣的静态数据,和/或动态计算的值。它可能会改变数据表示:比如将二进制 doubles 转换为 ASCII 表示,或者确保网络字节顺序而不管主机的字节序。它也可能会省略一些对象的数据,例如缓存条目、用于管理但不是数据逻辑部分的计数器、未填充的数组元素等。

    为什么要使用 reinterpret_cast 到 char*?在写入流时,我什么时候应该知道做这样的事情?

    write() 函数签名需要一个 const char* 参数,因此正在完成此转换。每当您无法以其他方式将 char* 获取到数据时,您就需要使用强制转换。

    转换反映了write() 将开始于对象第一个字节的数据视为 8 位值的方式,而不考虑数据的实际预转换类型。这与能够执行诸如 float 的最后一个字节的 write()double 的前 3 个字节出现在同一结构中的事情有关 - 所有数据边界和解释在reinterpret_cast&lt;&gt;.

    (实际上,在从输入流中对字节进行read() 处理时,您必须更加小心...假设您在写入内存时读取构成double 的数据,该数据未正确对齐double,然后尝试将其用作double,您的 CPU 可能会出现 SIGBUS 或类似的对齐异常,或者性能下降,具体取决于您的系统。)

    【讨论】:

      【解决方案2】:

      basic_ostream::write 及其对应的basic_istream::read 用于对数据流执行未格式化的 I/O。通常,这是原始二进制数据,可能包含也可能不包含可打印的 ascii 字符。

      read/write 与其他格式化运算符(如 &lt;&lt;&gt;&gt;getline 等)之间的主要区别在于前者不对正在处理的数据做出任何假设 - - 您可以完全控制从流中读取和写入哪些字节。与后者相比,后者可能会跳过空格、丢弃或忽略它们等。

      要回答您的第二个问题,reinterpret_cast &lt;char *&gt; 可以满足函数签名并一次处理一个字节的缓冲区。不要让char 类型欺骗你。使用char 的原因是因为它是语言提供的最小的内置原始类型。也许更好的名字是 uint8 来表明它确实是一个无符号字节类型。

      【讨论】:

      • 所以按照你的解释,理论上你可以将bool*传递给reinterpret_cast,因为它也是一个字节?
      • @0x499602D2:它不会是类型不匹配...bool* 就像任何其他指针...它有一定数量的字节(对于 32 位应用程序,通常为 4 , 8 表示 64 位),您可以将 reintrepret_castchar* 并从存储 bool 的地址开始写入字节。在 C++03 中,未指定 bool 的大小,但 C++11 引入了默认控件和程序员控件……它们没有什么特别之处,特别是 bool* 不是某种指向特定对象的指针内存中的位(如果可能的话,将转换为 char* 奇怪/有损)。
      猜你喜欢
      • 1970-01-01
      • 2014-08-27
      • 1970-01-01
      • 1970-01-01
      • 2020-12-02
      • 1970-01-01
      • 2018-03-03
      • 1970-01-01
      • 2020-06-08
      相关资源
      最近更新 更多