c++ 如果使用cout.rdbuf()来切换缓冲区并且从不将其设置回去,会出现什么问题?

mftmpeh8  于 2023-04-08  发布在  其他
关注(0)|答案(4)|浏览(228)

作者在标题A bus error on my platform下介绍了this code

#include <fstream>
#include <iostream>

int main()
{
    std::ofstream log("oops.log");
    std::cout.rdbuf(log.rdbuf());
    std::cout << "Oops!\n";
    return 0;
}

字符串“Oops!\n”被打印到文件“oops.log”。代码不会恢复cout的streambuf,但VS2010没有报告运行时错误。

cpjpxq1n

cpjpxq1n1#

由于logstd::cout共享一个缓冲区,因此该缓冲区可能会被释放两次(一次是在log超出作用域时,另一次是在程序终止时)。
这会导致未定义的行为,所以很难确切地解释为什么它在他的机器上触发了总线错误,而在你的机器上却默默地失败了。

tv6aics1

tv6aics12#

由于其他答案没有提到该怎么做,我将在这里提供。您需要保存和恢复cout应该管理的缓冲区。例如:

#include <fstream>
#include <iostream>

// RAII method of restoring a buffer
struct buffer_restorer {
    std::ios &m_s;
    std::streambuf *m_buf;

    buffer_restorer(std::ios &s, std::streambuf *buf) : m_s(s), m_buf(buf) {}
    ~buffer_restorer() { m_s.rdbuf(m_buf); }
};

int main()
{
    std::ofstream log("oops.log");
    buffer_restorer r(std::cout, std::cout.rdbuf(log.rdbuf()));
    std::cout << "Oops!\n";
    return 0;
}

现在当cout的缓冲区在cout在程序结束时被销毁之前被替换时,所以当cout销毁它的缓冲区时,正确的事情发生了。
对于简单地重定向标准io,通常环境已经有能力为你做这件事(例如,shell中的io重定向)。而不是上面的代码,我可能只是运行程序:

yourprogram > oops.log

还有一件事要记住的是,std::cout是一个全局变量,与其他全局变量有着相同的缺点。与其修改它,甚至使用它,你可能更喜欢使用常规技术来避免全局变量。例如,你可以传递一个std::ostream &log_output参数,并使用它,而不是让代码直接使用cout

lstz6jyr

lstz6jyr3#

您的程序具有未定义的行为。
全局cout对象的析构函数将在超出作用域时删除流缓冲区,log也是如此,它也拥有相同的流缓冲区。因此,您将删除相同的对象两次。
当一个程序有未定义的行为,任何事情都可能发生,从格式化你的硬盘驱动器终止没有任何错误。
例如,在我的平台上,程序从main()返回后进入无限循环。

1szpjjfi

1szpjjfi4#

好吧,我在我的应用程序的某些特定版本上收到了SEGMENTATION FAULT。应用程序在其中一个设备上运行正常,但当为另一个架构构建时:

Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x30 in...

我花了两天时间想弄清楚到底出了什么问题。

相关问题