如何制作一个不刷新 std::cout 的简单 C++ 程序

Posted

技术标签:

【中文标题】如何制作一个不刷新 std::cout 的简单 C++ 程序【英文标题】:How to make a simple C++ program in which std::cout is not flushed 【发布时间】:2016-09-30 11:11:32 【问题描述】:

为了更好地理解 C++ 中的缓冲流,我想编写一个简单的程序,其中 std::cout 缓冲区在终止前不会被刷新。因为我已经读到std::cout 在正常终止时被刷新,所以我尝试抛出一个运行时错误。我也避免使用std::endl,因为我知道这会强制刷新。第一次尝试:

//file noflush.cpp
#include <iostream>

int main() 
    std::cout << "Don't write me to the console!";
    throw 0;

用g++编译,从终端调用:

$ ./noflush
libc++abi.dylib: terminating with uncaught exception of type int
Don't write me to the console!Abort trap: 6

即使我强制出现运行时错误,缓冲区似乎仍会在终止时被刷新。是否可以将缓冲区中的一些数据“绞合”起来,不将其写入设备?

【问题讨论】:

只是说即使使用std::abort() 中止,缓冲区也会被刷新(使用 Apple LLVM 版本 6.0 (clang-600.0.57)(基于 LLVM 3.5svn)和 LLVM libc++),不过根据cppreference,它是实现定义的文件等开放资源是否关闭(在调用std::abort()时)。 【参考方案1】:

这不是标准的c++,但是在POSIX中,你可以发送一个“kill”信号来杀死正在运行的进程。这将停止执行而不进行清理,例如刷新缓冲区。

编辑:我意识到信号不仅是 POSIX,而且实际上是 C 标准库的一部分(并且包含在 C++ 标准库中)。

#include <csignal>
// ...
std::cout << "Don't write me to the console!";
std::raise(SIGKILL);

【讨论】:

如何取消引用空指针以引发空指针异常/运行时段错误?这可能是一个很好的跨平台终止进程的方法。 @kevinarpe 取消引用空指针具有未定义的行为,因此不能保证终止进程并且在技术上不可移植......但实际上,在取消引用空指针时崩溃 可能比 POSIX 得到更广泛的“支持”。这与 Caduchon 的回答基本相同,除了来自空指针取消引用的 UB 可能比取消引用和删除未初始化指针的 UB 更一致。 @kevinarpe 不能保证取消引用空指针时崩溃。 @Leandros 我怀疑 SIGKILL 上的缓冲区不刷新也不能保证。甚至是 SIGKILL 的存在。 @immibis 由 SIGKILL 的语义保证。程序无法处理 SIGKILL,因此无法运行清理代码来响应它。【参考方案2】:

据我所知,在程序终止之前没有符合标准且干净的方法来避免 std::coutflush()(但是,当然,您可以使用不干净的方法,例如发出信号 directly或indirectly)。根据cppreference 的说法,由std::cout 控制的实际缓冲区类型是实现定义的,但从std::streambuf 派生而来,这似乎不允许以模拟无声吞下缓冲区的方式进行公共访问。

此外,正如我在评论中指出的,即使程序异常终止(通过std::terminate()std::abort() 可能会或可能不会关闭打开的资源,所以这又是定义的实现。

【讨论】:

正确的引用是“全局对象 std::cout [...] 控制输出到实现定义类型的流缓冲区 [...]”(缓冲区而不是流对象是实现定义的)【参考方案3】:

通过以下示例,我可以使用 gcc 4.8.3 创建您想要的行为:

#include <iostream>
#include <vector>

int main()

    std::string str;
    for(unsigned long int i = 0; i < 10000; ++i)
        str += "Hello ! ";
    str += "END";
    std::cout << str;

    std::vector<double>* p;
    p->push_back(1.0);
    delete p;

    std::cout << "STILL ALIVE !" << std::endl;

    return 0;

那么,输出是:

你好!你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 !你好 ! [...] 你好 !分段错误

我们可以看到END在分段错误之前没有打印出来。

【讨论】:

Uhmm... 这个例子在循环中使用 10 而不是 10000 也能正常工作吗? 当我尝试使用 10 时,分段错误立即出现。因此,是的,它也有效,但更模糊。 不,字符串的结尾是"END"。它没有打印出来。然后部分打印字符串。缓冲区未正确刷新。我不明白你为此设置了-1! 循环是为了创建一个大字符串:更好地看到缓冲区问题的发生。用于简单检查所有字符串的“END”已打印。 “STILL ALIVE”,因为并不总是抛出分段错误(例如,如果您访问自己的内存,通常是增长的缓冲区)。保持冷静 ! -1 和您的评论同时发生,评论 a -1 是一个很好的做法。这很正常,我以为是你…… @Caduchon 仍然有礼貌地给评论者怀疑的好处。毕竟他们正在努力提供帮助,不幸的是,这个网站有很多不赞成投票的忍者。附言很抱歉完全跑题了。【参考方案4】:

如果我猜对了,你想捕获或忽略std::cout 的输出:

#include <iostream>
#include <sstream>
int main()

    // Capture the output to `std::cout`
    
        std::cout << "[Capture Output]" << std::endl;
        std::stringstream cpature;
        auto restore = std::cout.rdbuf(cpature.rdbuf());

        std::cout << "... captured output ..." << std::endl;

        std::cout.rdbuf(restore);
        std::cout << "[Enable Output]" << std::endl;

        // Display the cpatured output.
        std::cout << cpature.rdbuf();
    
    std::cout << std::endl;

    // Even more drasticly: Ignore the output to `std::cout`
    
        std::cout << "[Ignore Output]" << std::endl;
        auto restore = std::cout.rdbuf(nullptr);

        std::cout << "... ignored output ..." << std::endl;

        std::cout.rdbuf(restore);
        std::cout << "[Enable Output]" << std::endl;
    

    std::cout << "[End]\n";

【讨论】:

【参考方案5】:
#include <iostream>
#include <sstream>
#include <vector>
int main()

    std::stringstream cpature;
    auto restore = std::cout.rdbuf(cpature.rdbuf());    
    std::cout.rdbuf(restore);
    for(unsigned long int i = 0; i < 10000; ++i)
        std::cout <<"Hello ! "  << std::endl;
    std::cout << "END"  << std::endl;

    std::cout << cpature.rdbuf();
    std::vector<double> *p;
    p->push_back(1.0);
    delete p;
    std::cout << "STILL ALIVE !" << std::endl;

【讨论】:

以上是关于如何制作一个不刷新 std::cout 的简单 C++ 程序的主要内容,如果未能解决你的问题,请参考以下文章

cpp►endl与‘ ‘的区别?

C++ `endl` 与 ` ` 的区别

为啥我们需要绑定 std::cin 和 std::cout?

C 程序内存冲突取决于 std::cout (?)

C++:奇怪的 std::cout 错误

C++,三元运算符,std::cout