0

我知道cout几天前有缓冲区,当我google它时,据说缓冲区有点像堆栈,从右到左获取cout和printf的输出,然后将它们输出(到控制台或文件) 从上到下。像这样,

a = 1; b = 2; c = 3;
cout<<a<<b<<c<<endl;
buffer:|3|2|1|<-   (take “&lt;-” 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

看来stack的方式是对的~ 不过昨天看了C++ Primer Plus,据说cout是从左到右的,每次都返回一个object(cout),所以“这就是让你拼接输出的特性通过使用插入”。但是从左到右的方式不能解释 cout<

然后Alnitak告诉我,“<< 运算符实际上是 ostream& operator<<(ostream& os, int),所以另一种写法是: operator<< ( operator<< ( operator<< ( cout, a ), b ), C )”,

如果最正确的论点首先被评估,它可以得到一些解释。

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

4

5 回答 5

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内部表示。我建议您查看头文件以了解更多信息。

于 2009-03-20T06:10:21.413 回答
4

就像一般提示一样,永远不要在同一行中使用 i++ 作为 i 或 i-- 的另一个用法。

问题是函数参数可以按任何顺序计算,所以如果你的函数参数有任何副作用(例如递增和递减操作),你不能保证它们会按照你期望的顺序运行。这是要避免的。

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

函数1(函数2(foo),bar);

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

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

函数1(函数2(i++),i);

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

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

于 2009-03-20T06:33:54.557 回答
1

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

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

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

于 2009-03-20T06:20:19.813 回答
1

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

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

正如您所说的那样,您将调用约定与其他细节混淆了,它们与 std::cout 的缓冲完全无关。

于 2009-03-20T07:32:57.033 回答
0

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

于 2009-03-22T17:12:22.397 回答