为什么将重定向重定向到文件时发生段错误?

时间:2018-08-29 13:50:39

标签: c++

以下代码运行正常,但是当删除第9行cout.rdbuf(x);时,发生段故障。 大家能告诉我原因吗? 我对C ++不熟悉... 但是我必须在流出后直接继续指向out.txt。 我的环境是Ubuntu 12。 非常感谢!

#include <iostream>
#include <fstream>
using namespace std;
int main(void)
{
    ofstream outf("out.txt");
    streambuf* x = cout.rdbuf(outf.rdbuf());// redirect to out.txt
    cout << "Testn"<<endl;  // write to out.txt
    cout.rdbuf(x);  // recovery
    cout << "Test2n"<<endl; // write to screen
    return 0;
}

尽管所有内容都打印到out.txt中,但以下代码中发生了段错误。

#include <iostream>
#include <fstream>

using namespace std;
int main(void)
{
    ofstream outf("out.txt");
    streambuf* x = cout.rdbuf(outf.rdbuf());// redirect to out.txt
    cout << "Testn"<<endl;  // write to out.txt
//    cout.rdbuf(x);        // recovery
    cout << "Test2n"<<endl;     
    cout << "Test3n"<<endl; 
    cout << "Test4n"<<endl; 
    cout << "Test5n"<<endl; 
    //Test2n~Test5n is printed to out.txt, but segment fault occurs now.    
    return 0;
}

1 个答案:

答案 0 :(得分:0)

(如您所确定的),此问题是由于两个输出流共享一个缓冲区而关闭而导致的。我们可以通过编译代码并在Valgrind中运行它来看到这一点:

$ g++ -g -Wall -Wextra 52079058.cpp -o 52079058
$ valgrind -q --leak-check=full ./52079058
==27111== Invalid read of size 8
==27111==    at 0x4985EC8: pubsync (streambuf:278)
==27111==    by 0x4985EC8: std::ostream::flush() (ostream.tcc:219)
==27111==    by 0x491C0EB: std::ios_base::Init::~Init() (ios_init.cc:134)
==27111==    by 0x4BE28F0: __run_exit_handlers (exit.c:108)
==27111==    by 0x4BE29E9: exit (exit.c:139)
==27111==    by 0x4BCCB1D: (below main) (libc-start.c:344)

在这里,我们可以看到一个流正在尝试刷新到另一个流已删除的缓冲区。因此,在此之前,我们始终需要确保每个缓冲区完全由一个流拥有。


如果您需要确保std::cout的缓冲区已恢复,而不管其代码路径如何,那么您可能想创建一个基于范围的保护对象,该对象在被销毁后将恢复。

类似这样的东西:

#include <iostream>
#include <fstream>

class stream_redirection
{
    std::ostream& from;
    std::ofstream to;
    std::streambuf *const saved;
public:
    stream_redirection(std::ostream& from, const std::string& filename)
        : from{from},
          to{filename},
          saved{from.rdbuf(to.rdbuf())}
    {}
    stream_redirection(const stream_redirection&) = delete;
    void operator=(const stream_redirection&) = delete;
    ~stream_redirection()
    {
        from.rdbuf(saved);
    }
};

int main()
{
    {
        auto guard = stream_redirection(std::cout, "out.txt");

        std::cout << "Testn"<<std::endl;  // write to out.txt
        std::cout << "Test2n"<<std::endl;
        std::cout << "Test3n"<<std::endl;
    }
    std::cout << "Test4n"<<std::endl; // write to screen
    std::cout << "Test5n"<<std::endl;
}

如果直接在guard中创建main(),而不是在内部作用域(如我在此处演示的那样)中创建main(),那么重定向将在程序退出时清除(即,当{ {1}}返回)。


我认为必须创建这样的类表明程序中某处存在严重的设计缺陷。任何需要产生输出的子系统都应该传递一个写入该输出的流(有些子系统会走得更远,并认为日志记录和错误流也应该在需要的地方传递)。