cout的缓冲如何工作?

时间:2009-03-20 05:51:09

标签: c++ buffer cout

我知道cout几天前有缓冲区了,当我google它时,据说缓冲区有点像堆栈,从右到左得到cout和printf的输出,然后把它们放到外面(到控制台或文件)从顶部到bottem。像这样,

a = 1; b = 2; c = 3;
cout<<a<<b<<c<<endl;
buffer:|3|2|1|<-   (take “<-” as a poniter)

output:|3|2|<-     (output 1)
        |3|<-       (output 2)
        |<-         (output 3)

然后我写下面的代码,

#include <iostream> 
using namespace std; 
int c = 6;
int f() 
{   
    c+=1; 
    return c; 
} 

int main() 
{ 
     int i = 0; 
     cout <<"i="<<i<<" i++="<<i++<<" i--="<<i--<<endl; 
     i = 0;
     printf("i=%d i++=%d i--=%d\n" , i , i++ ,i-- );

     cout<<f()<<" "<<f()<<" "<<f()<<endl; 
     c = 6;
     printf("%d %d %d\n" , f() , f() ,f() );
     system("pause");
     return 0; 
}

在VS2005下,输出为

i=0 i++=-1 i--=0
i=0 i++=-1 i--=0
9 8 7
9 8 7

似乎堆栈方式是正确的〜 但是,我昨天读了C ++ Primer Plus,据说cout从左到右工作,每次都返回一个对象(cout),所以“这就是让你通过插入连接输出的功能”。但从左到右的方式无法解释cout&lt;

然后Alnitak告诉我,“&lt;&lt;运算符实际上是ostream&amp; operator&lt;&lt;(ostream&amp; os,int),所以另一种写法是: 运营商LT;&LT; (运算符&lt;&lt;(运算符&lt;&lt;(cout,a),b),c)“,

如果首先评估最正确的参数,可以解释一下。

现在我对cout缓冲区如何工作感到困惑,有人可以帮助我吗?

5 个答案:

答案 0 :(得分:11)

你混合了很多东西。到目前为止:

  • cout
  • 的实施细节
  • 链式电话
  • 调用约定

尝试单独阅读它们。并且不要一气呵成地考虑所有这些。

  

printf(“i =%d i ++ =%d i - =%d \ n”,i,i ++,i--);

以上行调用未定义的行为。阅读常见问题解答3.2。注意,您观察到的是函数调用约定的副作用以及特定实现(即您的)在堆栈中传递参数的方式。如果您在其他机器上工作,则不保证这一点。

我认为你将函数调用的顺序与缓冲混淆。当您有一个cout语句后跟多个插入<<时,您实际上是一个接一个地调用多个函数调用。所以,如果你要写:

cout << 42 << 0;

这实际上意味着:你打电话,

cout = operator<<(cout, 42)

然后在另一个调用中使用return给同一个运算符:

cout = operator<<(cout, 0)

您通过上述方法测试的内容不会告诉您任何cout的内部陈述。我建议你看看头文件了解更多。

答案 1 :(得分:4)

正如一般性提示一样,永远不要将i ++与i或i的另一种用法放在同一行 -

问题是函数参数可以按任何顺序计算,因此如果函数参数有任何副作用(例如递增和递减操作),则无法保证它们将按照您期望的顺序运行。这是要避免的事情。

这种情况也是如此,这类似于你的cout用法的实际扩展:

function1(function2(foo),bar);

编译器在调用function2之前可以自由地调整bar,反之亦然。例如,您可以保证function2将在调用function1之前返回,但不能保证它们的参数按特定顺序进行评估。

当您执行以下操作时,这会成为一个问题:

function1(function2(i ++),i);

您无法指定在“i ++”之前或之后是否评估“i”,因此您可能会得到与您预期不同的结果,或者使用不同编译器甚至不同版本的不同版本获得不同的结果相同的编译器。

底线,避免带有副作用的陈述。只有当它们是该行上的唯一语句或者您知道您只修改一次相同的变量时才使用它们。 (“行”表示单个语句加分号。)

答案 2 :(得分:1)

你看到的是未定义的行为。

本地i和全局c多次加/减,没有序列点。这意味着您获得的价值可以是任何事物。取决于编译器,可能还有处理器架构和内核数量。

cout缓冲区可以被认为是队列,所以Alnitak是对的。

答案 3 :(得分:1)

除了正确地指出您看到未定义行为的其他答案之外,我想我会提到std::cout使用std::streambuf类型的对象来执行其内部缓冲。基本上它是一个表示缓冲区的抽象类(大小特定于实现,对于非缓冲流缓冲区甚至可以为0)。编写std::cout的那个,当它“溢出”时,它会被刷新到stdout。

事实上,您可以更改与std::streambuf相关联的std::cout(或任何与此相关的流)。如果你想做一些聪明的事情,例如让所有std::cout调用以日志文件或其他内容结尾,这通常很有用。

正如dirkgently said你将调用约定与其他细节混淆,它们与std :: cout的缓冲完全无关。

答案 4 :(得分:0)

此外,混合输出范例(printf和cout)是特定于实现的。