clog,cout,cerr 输出机制

clog:控制输出,使其输出到一个缓冲区,这个缓冲区关联着定义在 <cstdio> 的 stderr。

cerr:强制输出刷新,没有缓冲区。

cout:控制输出,使其输出到一个缓冲区,这个缓冲区关联着定义在 <ostream> 的 stdout。

但是我们分别测试如下三个程序的结果如下:

cout:

#include <iostream>

using namespace std;

int main(int argc, char const *argv[])
{
    cout << "ERROR!!";
    while (true);
    return 0;
}

输出结果为:

    

可以理解,因为 cout 输出是有缓冲区的,这里没有输出说明缓冲区还没有刷新。

cerr:

#include <iostream>

using namespace std;

int main(int argc, char const *argv[])
{
    cerr << "ERROR!!";
    while (true);
    return 0;
}

输出:

    

可以理解,因为 cerr 没有输出缓冲区,是强制刷新的,所以在循环之前就已经刷新了,所以会打印结果。

clog:

#include <iostream>

using namespace std;

int main(int argc, char const *argv[])
{
    clog << "ERROR!!";
    while (true);
    return 0;
}

输出:

    

这个比较奇怪,不是说 clog 是由缓冲区的嘛,为什么它不是像 cout 一样的输出呢?先看下面部分:

对于cout,clog,cerr输出的机制,c跟c++的标准做法是:

stderr : 预设没有分配 buffer

stdout :预设有分配 buffer

cout :用 stdout 的buffer

clog :用 stderr 的 buffer

cerr :用 stderr 的buffer,强制清空 buffer

其中,stderr 的 buffer 预设为没有,所以 clog 和 cerr 都是直接在屏幕输出而没有在缓冲区驻留。所以前面 clog 的输出结果也就可以理解了,因为缓冲区是空的,那么输出就不会在缓冲区驻留了。

下面来说说 cout,clog,cerr 的输出顺序,也可作为 clog 一个更深入的理解。

先看下面一段代码:

#include <iostream>

using namespace std;

int main(int argc, char const *argv[])
{
    setbuf(stderr,0);
    cout << "123";
    clog << "456";
    cerr << "789";

    return 0;
}

它的输出结果是:

  456123789

上面代码中,setbuf 为 0,也就是设置 stderr 的缓冲区为 0,因为 stderr 的值是有可能因为编译器的不同而不同的,前面说的 c++ 机制只是标准机制,编译器实现商可以进行自己的改变的。

现在来理解一下输出的结果,首先 clog 的输出在最前面很容易理解,但是为什么 cout 的输出在 cerr 的前面呢,因为cerr 的输出关联到了 cout,也即 cerr.tie()=&cout,所以当 cerr 要清空时,会先清空前面的 cout 的缓冲区。[注意只是 cerr 前面的 cout ]

看另一段代码:

#include <iostream>

using namespace std;

char buf[10];
int main(int argc, char const *argv[])
{
    setbuf(stderr,buf);
    cout << "123";
    clog << "456";
    cerr << "789";

    return 0;
}

此时的输出是:

  123456789

在这里给 stderr 的缓冲区设为了 buf,不再是 0 了,所以这里输出结果按顺序输出。

下面取消 cerr 和 cout 的关联:

#include <iostream>

using namespace std;

char buf[10];
int main(int argc, char const *argv[])
{
    cerr.tie(0);
    setbuf(stderr, buf);
    cout << "123";
    clog << "456";
    cerr << "789";

    return 0;
}

输出结果为:

  456789123

这里 clog 在 cout 前面输出,因为 cerr 输出的时候强制刷新了 stderr 缓冲区。

cerr 和 clog 位置调换,结果为:

#include <iostream>

using namespace std;

char buf[10];
int main(int argc, char const *argv[])
{
    cerr.tie(0);
    setbuf(stderr, buf);
    cout << "123";
    cerr << "789";
    clog << "456";

    return 0;
}

输出结果:

  789123456

此时 cerr 最先输出,因为强制刷新,而 clog 因为缓冲区不为空,所以会在程序结束的时候执行 flush。

 

再来说说 clog 和 cerr 的重定向问题。

直接对 cerr 和 clog 的输出使用重定向符 > 是无效的,这两个的输出一定会打印在终端上,但是如果一定要进行重定向,那么可以用  rdbuf 函数,如下:

ofstream ofs("logfile");
clog.rdbuf(ofs.rdbuf());
clog << "Goes to file." << endl;

 

以上测试结果都是在mac g++4.2.1 测试的结果,输出结果在mac的终端。在windows下面测试的结果可能不同。 

具体的区别可以参考如下内容:【以下内容来源:http://tieba.baidu.com/p/937433213】

1.该物件所用的stream是否按照规定分配buffer
全缓冲:buffer满了才执行fflush()
行缓冲:buffer满了or碰到换行字元才执行fflush()
无缓冲:不管buffer有没有满都执行fflush()


console下
            标准     windows    linux
stdout| 行/无缓冲|全缓冲   |行缓冲    |
stderr| 行/无缓冲|全缓冲   |无缓冲    |

非console
            标准      windows   linux
stdout|  全缓冲    |全缓冲   |全缓充    |
stderr|  行/无缓冲|全缓冲   |无缓冲    |

以上是预设值,有一点要厘清,全/行/无缓冲只是该stream的特性,
与它有没有buffer并没有关系,拿windows来说,
乍看之下会等到buffer满才输出,但实际上是直接输出,
因为没有分配buffer,你也可以对无缓冲的stream分配buffer,
虽然一样直接跑fflush()就是了

如果要分配buffer给stream,可以用setbuf()和setvbuf(),
用前者时要注意,一但用它分配buffer后,行缓冲的特性会消失,
且buffer不能是局部变量,因为最后一次fflush()是在exit()里执行
那时局部变量已经被释放


2.该物件是否按照规定设置ios::unitbuf
如果有设ios::unitbuf,就直接执行fflush(),
即使底下stream设成全缓冲跟分配buffer也一样,
除非手动设定,否则只有cerr才有设ios::unitbuf

如果要修改物件的ios::unitbuf,可用setf()或unsetf()设定


3.该物件的是否按照规定绑定别的物件

              标准  windows  linux
clog.tie()| null |&cout|   null |
cerr.tie()|&cout|&cout|  &cout|
cout.tie()|null  | null  |    null |

如果有绑别的物件,当自己准备要跑fflush()时,
会先让被绑定的物件执行fflush(),然后才换自己跑fflush()

如果要修改绑定的物件,就用tie()来设,传0代表不绑定

虽然下列网站写cin、cerr、clog都绑定cout, 
http://www.cplusplus.com/reference/iostream/ios/tie/
但是C++的规格书上,其实没规定clog要绑cout

4.换行字元跟endl
前者只能让行缓冲的物件跑fflush(),
后者则强迫cout、cerr、clog跑fflush()

5.如果一直到main()结束都没有跑fflush()
那么stdout会先输出,接著才是stderr

7.main()结束后,不可再用cout、cerr、clog来输出
首先来看exit()的标准流程

A.摧毁thread物件
B.摧毁global物件/摧毁函式里的static物件/执行atexit注册函式
C.让所有的C stream执行fflush(),接著关闭它们
D.砍掉暂存档
E.归还控制权给OS


在执行dtor/atexit注册函式时,无法保证cout、cerr、clog这些物件还在

总结:
没分配buffer、无缓冲、ios::unitbuf!=0、buffer已满、行缓冲碰到'\n'、endl
以上只要有一个成立就会跑fflush()

posted @ 2015-03-19 10:53  厕所门口~~  阅读(1287)  评论(1编辑  收藏  举报