先抓出std::endl的源代码:

/**

*  @file  ostream

*  @brief  Write a newline and flush the stream.

*

*  This manipulator is often mistakenly used when a simple newline is

*  desired, leading to poor buffering performance.  See

*  http://gcc.gnu.org/onlinedocs/libstdc++/manual/bk01pt11ch25s02.html

*  for more on this subject.

*/

template<typename _CharT, typename _Traits>

inline basic_ostream<_CharT, _Traits>&

endl(basic_ostream<_CharT, _Traits>& __os)

{

return flush(__os.put(__os.widen('\n')));

}

可以看到endl实际是名字空间std中的一个全局内联函数(记住std::endl是一个函数),它做了两件事:

1) 输出一个换行符(为何要输出一个换行符,如果不输出会怎么样?);

2) 调用flush。

继续看flush这个函数(也是std名字空间内的全局内联函数):

/**

*  @brief  Flushes the output stream.

*

*  This manipulator simply calls the stream's @c flush() member function.

*/

template<typename _CharT, typename _Traits>

inline basic_ostream<_CharT, _Traits>&

flush(basic_ostream<_CharT, _Traits>& __os)

{

return __os.flush(); // 注意这里的os不是操作系统的意思,而是类basic_ostream的缩写

}

进一步查看std::basic_ostream::flush的代码:

/**

*  @file  ostream.tcc

*/

template<typename _CharT, typename _Traits>

basic_ostream<_CharT, _Traits>&

basic_ostream<_CharT, _Traits>::flush()

{

// _GLIBCXX_RESOLVE_LIB_DEFECTS

// DR 60. What is a formatted input function?

// basic_ostream::flush() is *not* an unformatted output function.

ios_base::iostate __err = ios_base::goodbit;

__try

{

// 刷新发生在pubsync,底层调用的是LIBC库函数fflush

if (this->rdbuf() && this->rdbuf()->pubsync() == -1)

__err |= ios_base::badbit;

}

__catch(__cxxabiv1::__forced_unwind&)

{

this->_M_setstate(ios_base::badbit);

__throw_exception_again;

}

__catch(...)

{

this->_M_setstate(ios_base::badbit);

}

if (__err)

this->setstate(__err);

return *this;

}

下段小代码,可以看到两个帮助释疑的调用栈,:

$ cat xxx.cpp

#include <iostream>

int main() {

std::cout << std::endl;

return 0;

}

写换符符:

#0  0x00007ffff72e0840 in write () from /lib64/libc.so.6

#1  0x00007ffff726cfb3 in _IO_new_file_write () from /lib64/libc.so.6

#2  0x00007ffff726e41c in __GI__IO_do_write () from /lib64/libc.so.6

#3  0x00007ffff726e7f3 in __GI__IO_file_overflow () from /lib64/libc.so.6

#4  0x00007ffff726ac49 in putc () from /lib64/libc.so.6

#5  0x00007ffff7b694c6 in std::ostream::put(char) () from /lib64/libstdc++.so.6

#6  0x00007ffff7b69712 in std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&) ()

from /lib64/libstdc++.so.6

#7  0x0000000000400803 in main () at xxx.cpp:3

刷新流:

#0  0x00007ffff7262030 in fflush () from /lib64/libc.so.6

#1  0x00007ffff7b68d5e in std::ostream::flush() () from /lib64/libstdc++.so.6

#2  0x0000000000400803 in main () at xxx.cpp:3

实际上,还可以看到更多,如全局对象std::cout、std::cerr和std::clog等的析构:

#0  0x00007ffff7262030 in fflush () from /lib64/libc.so.6

#1  0x00007ffff7b68d5e in std::ostream::flush() () from /lib64/libstdc++.so.6

#2  0x00007ffff7b41bc8 in std::ios_base::Init::~Init() () from /lib64/libstdc++.so.6

#3  0x00007ffff722fa69 in __run_exit_handlers () from /lib64/libc.so.6

#4  0x00007ffff722fab5 in exit () from /lib64/libc.so.6

#5  0x00007ffff7218c0c in __libc_start_main () from /lib64/libc.so.6

#6  0x0000000000400729 in _start ()

Init析构的同时析构cout等:

/**

*  @file  ios_init.cc

*/

ios_base::Init::~Init()

{

// Be race-detector-friendly.  For more info see bits/c++config.

_GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_S_refcount);

if (__gnu_cxx::__exchange_and_add_dispatch(&_S_refcount, -1) == 2)

{

_GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_S_refcount);

// Catch any exceptions thrown by basic_ostream::flush()

__try

{

// Flush standard output streams as required by 27.4.2.1.6

cout.flush();

cerr.flush();

clog.flush();

#ifdef _GLIBCXX_USE_WCHAR_T

wcout.flush();

wcerr.flush();

wclog.flush();

#endif

}

__catch(...)

{

}

}

}

文件ios_init.cc其它相关函数源码:

1) 类std::ios_base::Init的构造函数

ios_base::Init::Init()

{

if (__gnu_cxx::__exchange_and_add_dispatch(&_S_refcount, 1) == 0)  //  防止重复初始化

{

// Standard streams default to synced with "C" operations.

_S_synced_with_stdio = true;

new (&buf_cout_sync) stdio_sync_filebuf<char>(stdout);

new (&buf_cin_sync) stdio_sync_filebuf<char>(stdin);

new (&buf_cerr_sync) stdio_sync_filebuf<char>(stderr);

// The standard streams are constructed once only and never destroyed.

new (&cout) ostream(&buf_cout_sync);

new (&cin) istream(&buf_cin_sync);

new (&cerr) ostream(&buf_cerr_sync);

new (&clog) ostream(&buf_cerr_sync);

cin.tie(&cout);

cerr.setf(ios_base::unitbuf);

// _GLIBCXX_RESOLVE_LIB_DEFECTS

// 455. cerr::tie() and wcerr::tie() are overspecified.

cerr.tie(&cout);

#ifdef _GLIBCXX_USE_WCHAR_T

new (&buf_wcout_sync) stdio_sync_filebuf<wchar_t>(stdout);

new (&buf_wcin_sync) stdio_sync_filebuf<wchar_t>(stdin);

new (&buf_wcerr_sync) stdio_sync_filebuf<wchar_t>(stderr);

new (&wcout) wostream(&buf_wcout_sync);

new (&wcin) wistream(&buf_wcin_sync);

new (&wcerr) wostream(&buf_wcerr_sync);

new (&wclog) wostream(&buf_wcerr_sync);

wcin.tie(&wcout);

wcerr.setf(ios_base::unitbuf);

wcerr.tie(&wcout);

#endif

// NB: Have to set refcount above one, so that standard

// streams are not re-initialized with uses of ios_base::Init

// besides <iostream> static object, ie just using <ios> with

// ios_base::Init objects.

__gnu_cxx::__atomic_add_dispatch(&_S_refcount, 1);

}

}

2) 全局函数ios_base::sync_with_stdio

bool

ios_base::sync_with_stdio(bool __sync)

{

// _GLIBCXX_RESOLVE_LIB_DEFECTS

// 49.  Underspecification of ios_base::sync_with_stdio

bool __ret = ios_base::Init::_S_synced_with_stdio;

// Turn off sync with C FILE* for cin, cout, cerr, clog iff

// currently synchronized.

if (!__sync && __ret)

{

// Make sure the standard streams are constructed.

ios_base::Init __init;

ios_base::Init::_S_synced_with_stdio = __sync;

// Explicitly call dtors to free any memory that is

// dynamically allocated by filebuf ctor or member functions,

// but don't deallocate all memory by calling operator delete.

buf_cout_sync.~stdio_sync_filebuf<char>();

buf_cin_sync.~stdio_sync_filebuf<char>();

buf_cerr_sync.~stdio_sync_filebuf<char>();

#ifdef _GLIBCXX_USE_WCHAR_T

buf_wcout_sync.~stdio_sync_filebuf<wchar_t>();

buf_wcin_sync.~stdio_sync_filebuf<wchar_t>();

buf_wcerr_sync.~stdio_sync_filebuf<wchar_t>();

#endif

// Create stream buffers for the standard streams and use

// those buffers without destroying and recreating the

// streams.

new (&buf_cout) stdio_filebuf<char>(stdout, ios_base::out);

new (&buf_cin) stdio_filebuf<char>(stdin, ios_base::in);

new (&buf_cerr) stdio_filebuf<char>(stderr, ios_base::out);

cout.rdbuf(&buf_cout);

cin.rdbuf(&buf_cin);

cerr.rdbuf(&buf_cerr);

clog.rdbuf(&buf_cerr);

#ifdef _GLIBCXX_USE_WCHAR_T

new (&buf_wcout) stdio_filebuf<wchar_t>(stdout, ios_base::out);

new (&buf_wcin) stdio_filebuf<wchar_t>(stdin, ios_base::in);

new (&buf_wcerr) stdio_filebuf<wchar_t>(stderr, ios_base::out);

wcout.rdbuf(&buf_wcout);

wcin.rdbuf(&buf_wcin);

wcerr.rdbuf(&buf_wcerr);

wclog.rdbuf(&buf_wcerr);

#endif

}

return __ret;

}

前面提到的endl为何要输出一个换行符,这是因为fflush只是将数据刷到标准输出,标准输出本身也是有缓存的,而且默认是行缓存_IOLBF line buffered)。

是否可将标准输出设置为全缓存_IOFBF fully buffered)了?答案是可以,执行下列代码即可得到结果。

$ cat eee.cpp

#include <stdio.h>

int main(void) {

char buf[BUFSIZ];

setvbuf(stdout, buf, _IOFBF, BUFSIZ);

printf("Hello, world!\n");

getchar();

return 0;

}

是否行缓存和全缓存,与是否为字符设备(Character Device,只能顺序读取)或块设备(Block Device,支持随机存取)无关。

将标准输出定向到普通文件,则缓存类型同普通文件,即默认全缓存:

$ cat eee.cpp

#include <fcntl.h>

#include <stdio.h>

#include <stdlib.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <unistd.h>

int main(int argc, char* argv[]) {

char buf[BUFSIZ];

int fd = open("/tmp/xxx.txt", O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU);

if (fd == -1) {

perror("open");

return 1;

} else {

int fdnew = dup2(fd, STDOUT_FILENO); // 让标准输出指向文件

if (fdnew == -1) {

perror("dup2");

return 1;

} else {

printf("123\n");

getchar();

return 0;

}

}

}

匿名管道默认是全缓存,可用下列代码验证:

$ cat xxx.cpp

#include <fcntl.h>

#include <stdio.h>

#include <stdlib.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <sys/wait.h>

#include <unistd.h>

int main(int argc, char* argv[]) {

int fd[2];

char buf[BUFSIZ];

if (pipe(fd) == -1) {

perror("pipe");

exit(1);

}

pid_t pid = fork();

if (pid == -1) {

perror("fork");

exit(1);

} else if (pid == 0) { // Child process

dup2(fd[1], STDOUT_FILENO);

//setvbuf(stdout, buf, _IOLBF, BUFSIZ);

printf("hello\n");

getchar();

fflush(stdout);

_exit(0);

} else { // Parent process

char msg[BUFSIZ];

int n = read(fd[0], msg, sizeof(msg)-1);

if (n == -1) {

perror("Parent read");

} else {

msg[n] = '\0';

fprintf(stdout, "(%d)%s\n", n, msg);

}

wait(NULL);

exit(0);

}

}

有名管道和套接字(包含UNIX套接字也是全缓存),附问题:如何在GDB中调试跟踪std::cout?

C++标准库中的std::endl究竟做了什么?的更多相关文章

  1. STL笔记(6)标准库:标准库中的排序算法

    STL笔记(6)标准库:标准库中的排序算法 标准库:标准库中的排序算法The Standard Librarian: Sorting in the Standard Library Matthew A ...

  2. 通过atomic_flag简单自旋锁实现简单说明标准库中锁使用的memory_order

    在使用标准库中的加锁机制时,例如我们使用std::mutex,写了如下的代码(下面的代码使用condition_variable可能更合适) std::mutex g_mtx; int g_resNu ...

  3. 用CAS操作实现Go标准库中的Once

    Go标准库中提供了Sync.Once来实现"只执行一次"的功能.学习了一下源代码,里面用的是经典的双重检查的模式: // Once is an object that will p ...

  4. 彻底弄清c标准库中string.h里的常用函数用法

    在我们平常写的c/c++程序,一些算法题中,我们常常会用到c标准库中string.h文件中的函数,这些函数主要用于处理内存,字符串相关操作,是很有用的工具函数.而且有些时候,在笔试或面试中也会出现让你 ...

  5. Python 标准库中的装饰器

    题目描述 1.简单举例 Python 标准库中的装饰器 2.说说你用过的 Python 标准库中的装饰器 1. 首先,我们比较熟悉,也是比较常用的 Python 标准库提供的装饰器有:property ...

  6. (转)python标准库中socket模块详解

    python标准库中socket模块详解 socket模块简介 原文:http://www.lybbn.cn/data/datas.php?yw=71 网络上的两个程序通过一个双向的通信连接实现数据的 ...

  7. c/c++标准库中的文件操作总结

    1 stdio.h是c标准库中的标准输入输出库 2 在c++中调用的方法 直接调用即可,但是最好在函数名前面加上::,以示区分类的内部函数和c标准库函数. 3 c标准输入输出库的使用 3.1 核心结构 ...

  8. C标准库中atoi的一种可能的实现

    为避免与标准库中的atoi产生歧义, 我将自己编写的函数命名为strToInt, 以下是示例代码 #include <stdio.h> int strToInt(const char *s ...

  9. php标准库中的优先队列SplPriorityQueue怎么使用?(继承)

    php标准库中的优先队列SplPriorityQueue怎么使用?(继承) 一.总结 1.new对象,然后通过insert方法和extract方法来使用,top方法也很常用. 2.类的话首先想到继承, ...

随机推荐

  1. ROS源更改

    ROS源更改 配置你的电脑使其能够安装来自 packages.ros.org 的软件,使用国内或者新加坡的镜像源,这样能够大大提高安装下载速度 sudo sh -c '. /etc/lsb-relea ...

  2. CentOS中设置Apache服务器网站访问日志[每天的日志]

    在阿里云的linux 服务器下Apache的日志默认设置是七天更新一次, 并且所在的目录无法通过FTP浏览器查看, 这样让小白操作起来非常麻烦 可以使用rotatelogs来设置服务器的网站访问日志按 ...

  3. 【docker】centos7 上拉取docker镜像,一直拉取不到,报错:Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request canceled while w

    镜像拉取一直报错: Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request cancel ...

  4. C#中的System.Type和System.RuntimeType之间的区别

    string str = string.Empty; Type strType = str.GetType(); Type strTypeType = strType.GetType(); strTy ...

  5. dotnet core 之 gRPC

    dotnet core gRPC 原文在本人公众号中,欢迎关注我,时不时的会分享一些心得 HTTP和RPC是现代微服务架构中很常用的数据传输方式,两者有很多相似之处,但是又有很大的不同.HTTP是一种 ...

  6. tkiner将字典用在单选上

    from tkinter import * def printSelection(): print(cities[int(var.get())]) lab.config(text="你选择了 ...

  7. Spring IOC 复习

    Inversion of Control 将创建对象的权利交给框架,包括DI(Dependency Injection,依赖注入)和DL(Dependency Lookup,依赖查找),能削减计算机程 ...

  8. 搜索引擎elasticsearch监控利器cat命令

    目录 一.Cat通用参数 二.cat命令 三.示例 查询aurajike索引下的总文档数和有效文档数 查询aurajike各分片的调度情况 一.Cat通用参数 参数名 指令示例 功能 Verbose ...

  9. Spring Cloud Gateway转发Spring WebSocket

    Spring Cloud Gateway转发Spring WebSocket 源码:https://github.com/naah69/SpringCloud-Gateway-WebSocket-De ...

  10. 关于Git的用法

    关于Git Git 是一个分布式版本控制软件,与CVS.Subversion一类的集中式版本控制工具不同,它采用了分布式版本库的作法,不需要服务器端软件,就可以运作版本控制,使得源代码的发布和交流极其 ...