如何制作一个不刷新 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::cout
到 flush()
(但是,当然,您可以使用不干净的方法,例如发出信号 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++ 程序的主要内容,如果未能解决你的问题,请参考以下文章