先抓出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. VUE的$refs和$el的使用

    ref 被用来给元素或子组件注册引用信息 ref 有三种用法: 1.ref 加在普通的元素上,用this.$refs.(ref值) 获取到的是dom元素 2.ref 加在子组件上,用this.$ref ...

  2. 通过四个问题了解HTTP协议基础

    很多人都知道学习和理解HTTP协议的重要性及必要性,但HTTP相关知识对计算机基础较差,尤其是我这种没有计算机基础的人来说更是晦涩难懂 乘着最近有空闲时间,开始恶补HTTP相关基础知识,下面请跟着我通 ...

  3. RabbitMQ实例C#

    驱动组件.NET版本 官网推荐驱动:RabbitMQ.Client https://www.rabbitmq.com/devtools.html#dotnet-dev Connection和Chann ...

  4. SCCM+WSUS的方式分发补丁

    简单来说,System Center Configuration Manager(SCCM/ConfigMgr)由SMS(Systems Management Server)发展而来,其作为一款针对企 ...

  5. 华为手机usb调试打开后自动关闭怎么办?华为手机 usb调试为什么自动关闭?usb调试老是自动关闭怎么回事?

    01 解决方法一依次点击“设置”——“系统”——“开发人员选项”先开启“开发者选项”开关. 02 然后在开启“USB调试”开关后,一并将“'仅充电'模式下允许ADB调试”选项开关打开.这样,华为手机u ...

  6. MySQL修炼之路一

    1. MySQL概述 1. 什么是数据库 存储数据的仓库 2. 都有哪些公司在用数据库 金融机构.游戏网站.购物网站.论坛网站 ... ... 3. 提供数据库服务的软件 1. 软件分类 MySQL. ...

  7. OAuth 2.0 的四种授权模式

    RFC 6749 OAuth 2.0 的标准是 RFC 6749 文件.该文件先解释了 OAuth 是什么. OAuth 引入了一个授权层,用来分离两种不同的角色:客户端和资源所有者.......资源 ...

  8. 创建readonly只读用户脚本

    身为一名运维工作人员,保证服务器的安全是必要项,当开发人员或测试人员需登录到服务器查看日志等操作时,可只给定特定的权限防止误操作的惨况产生. 以下脚本内容均为我本人环境,如有更改可自行修改. ~]# ...

  9. DF1协议简述

    DF1协议 1.    概述 可编程控制器(PLC)因编程方便,抗干扰能力强,被广泛应用于各种领域.DF1协议是AB公司可编程控制器系统广泛支持的数据链路层通信协议,各系列可编程控制器及装有RSLin ...

  10. pyecharts的使用

    折线图1 import pyecharts.options as opts from pyecharts.charts import Line ​ x_data = ["Mon", ...