【问题标题】:Why does my buffer take more bytes than allowed?为什么我的缓冲区占用的字节数超过了允许的字节数?
【发布时间】:2013-06-27 20:59:27
【问题描述】:

我将stringstream 的缓冲区设置为 5 字节缓冲区。只有当我调用sputn 时,它才能读取比我想要的更多的字符。这是为什么呢?

#include <iostream>
#include <sstream>

int main()
{
    std::stringstream ss;
    char buf[5];

    ss.rdbuf()->pubsetbuf(buf, sizeof buf);
    ss.rdbuf()->sputn("hello world", 12);

    std::cout << ss.rdbuf(); // prints "Hello world"
}

【问题讨论】:

  • 欢迎来到 C。C 中没有任何东西可以阻止你在脚下射击自己。这意味着 c++ 不会为您进行边界检查。
  • @andre:虽然这不是原始数组访问,但这是std::basic_ostream 的成员。它真的不应该写通过数组的末尾。
  • @MooingDuck 我不认为sputn 有任何保证它不会写入缓冲区之外。
  • @andre:排序。 If the output sequence write position is not available, returns overflow(traits::to_int_-type(c)). 另一方面,pubsetbuf 似乎不需要记住它的缓冲区是多少位,因此它会声称写入位置始终可用>。
  • @MemyselfandI,你用的是什么编译器?我想知道在这种情况下编译器的行为是合理的......

标签: c++ file iostream


【解决方案1】:

首先,请注意pubsetbuf 是实现定义的。在 gcc 上它设置了一个新缓冲区,但例如在 MSVC 上什么都没有发生(它调用基类 setbuf 什么都不做)。

现在如here所说,sputn调用overflow(或通过其他方式达到调用的效果):

如果 put 区域已满 (pptr() == epptr()),此函数可能 调用overflow(),或者通过一些实现调用overflow()的效果 其他未指定的意思。

现在根据overflow 的文档:

确保放置区至少有一个字符的空间 通过保存从 pbase() 开始的一些初始字符子序列 到输出序列并更新指向输出区域的指针 (如果需要的话)。如果 ch 不是 traits::eof() (即 traits::eq_int_type(c, traits::eof()) != true),要么放到输出区要么 直接保存到输出序列中。

该函数可能会更新 pptr、epptr 和 pback 指针以定义 写入更多数据的位置。失败时,该功能确保 pptr() == nullptr 或 pptr() == epptr。

基本上,这意味着可以适当地调整缓冲区的大小以适应更多数据,而使用 gcc 这正是正在发生的事情。这是实际代码take from here

const __size_type __opt_len = std::max(__size_type(2 * __capacity), __size_type(512));
const __size_type __len = std::min(__opt_len, __max_size);

如您所见,它要么将容量加倍,要么将其设置为512 的大小,直到达到字符串缓冲区可以达到的最大大小。

【讨论】:

  • Interesting。你赢了。
  • @MooingDuck:从技术上讲,它是实现定义的,但在 gcc 上这是行为。
【解决方案2】:

C/C++ 不做任何明确的边界检查,所以内存溢出是可能的(而且很容易做到)。如果您通过 Valgrind 之类的调试器运行代码,它会告诉您正在读取未初始化的内存。

【讨论】:

  • 这是未定义的行为吗?我怎样才能防止这种情况发生?可能存在我不知道字节数组长度的情况。
  • 您必须在代码中实现手动错误检查。例如,在将字符串放入缓冲区之前检查字符串的长度。 slibc (code.google.com/p/slibc) 的网站提供了 C11 新边界检查的实现,有一些很好的例子来说明可能出错的地方。它们在 C 中,但概念是相同的。
【解决方案3】:

您的缓冲区仍然是 5 个字节长,并且不会占用超过允许的字节数,但是放入 12 个字节将“只是”执行可能导致软件编程出现很多问题的边界写入(崩溃、UB、数据损坏...)

strlen 可用于在复制前检查您写入的内容与缓冲区大小的对比。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-18
    • 1970-01-01
    • 1970-01-01
    • 2014-07-12
    • 1970-01-01
    相关资源
    最近更新 更多