Как работает буфер cout? - PullRequest
       9

Как работает буфер cout?

0 голосов
/ 20 марта 2009

Я знаю, что у cout есть буфер несколько дней назад, и когда я захожу в Google, говорят, что буфер похож на стек и получает выходные данные cout и printf справа налево, затем выводит их (в консоль или файл) сверху вниз. Вот так

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 <

Тогда Alnitak скажите мне, что «Оператор << действительно ostream & operator << (ostream & os, int), так что еще один способ написать это: operator << (operator << (operator << (cout, a), b), c) ", </p>

Если самый правильный аргумент оценивается первым, его можно объяснить.

Теперь я не понимаю, как работает буфер cout, кто-нибудь может мне помочь?

Ответы [ 5 ]

11 голосов
/ 20 марта 2009

Вы смешиваете много вещей. На сегодняшний день:

  • Детали реализации cout
  • Цепные звонки
  • Соглашения о вызовах

Попробуйте прочитать их отдельно. И не думай обо всех сразу.

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

Приведенная выше строка вызывает неопределенное поведение. Прочитайте FAQ 3.2 . Обратите внимание, что вы наблюдаете побочный эффект соглашения о вызове функции и способа передачи параметров в стеке конкретной реализацией (т. Е. Вашей). Это не гарантируется, если вы работали на других машинах.

Я думаю, вы путаете порядок вызовов функций с буферизацией. Если у вас есть оператор cout, за которым следуют несколько вставок <<, вы фактически вызываете несколько вызовов функций, один за другим. Итак, если бы вы написали:

cout << 42 << 0;

Это действительно означает: вы звоните,

cout = operator<<(cout, 42)

, а затем использовать возврат в другом вызове того же оператора, что и:

cout = operator<<(cout, 0)

То, что вы проверили выше, ничего не скажет вам о внутреннем представлении cout. Я предлагаю вам взглянуть на файлы заголовков, чтобы узнать больше.

4 голосов
/ 20 марта 2009

Так же, как общий совет, никогда не используйте i ++ в одной строке с другим использованием i или i -.

Проблема в том, что аргументы функции могут оцениваться в любом порядке, поэтому, если у аргументов функции есть какие-либо побочные эффекты (например, операции увеличения и уменьшения), вы не можете гарантировать, что они будут работать в том порядке, в котором вы ожидаете. Этого следует избегать.

То же самое относится и к этому случаю, который похож на фактическое расширение использования вашей cout:

function1 (function2 (foo), bar);

Компилятор может свободно вычислять bar перед вызовом function2 или наоборот. Например, вы можете гарантировать, что function2 вернется до вызова function1, но не в том, что их аргументы вычисляются в определенном порядке.

Это становится проблемой, когда вы делаете что-то вроде:

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

Вы не можете указать, будет ли «i» оцениваться до или после «i ++», поэтому вы, скорее всего, получите результаты, которые отличаются от ожидаемых, или разные результаты с разными компиляторами или даже с разными версиями тот же компилятор.

Итог, избегайте высказываний с побочными эффектами. Используйте их только в том случае, если они являются единственным оператором в строке или если вы знаете, что изменяете одну и ту же переменную только один раз. («Строка» означает одно утверждение плюс точку с запятой.)

1 голос
/ 20 марта 2009

В дополнение к другим ответам, которые правильно указывают на то, что вы видите неопределенное поведение, я решил упомянуть, что std::cout использует объект типа std::streambuf для внутренней буферизации. По сути, это абстрактный класс, представляющий буфер (размер зависит от реализации и может даже быть 0 для буферов буфера без буферизации). Единица для std::cout написана так, что когда она «переполняется», она сбрасывается в стандартный вывод.

Фактически, вы можете изменить std::streambuf, связанный с std::cout (или любым потоком по этому вопросу). Это часто полезно, если вы хотите сделать что-то умное, например, завершить все вызовы std::cout в файле журнала или что-то в этом роде.

И как прямо сказал вы путаете соглашение о вызовах с другими деталями, они совершенно не связаны с буферизацией std :: cout.

1 голос
/ 20 марта 2009

То, что вы видите, является неопределенным поведением.

Локальные i и глобальные c складываются / вычитаются несколько раз без точки последовательности. Это означает, что ценности, которые вы получаете, могут быть обо всем. Зависит от компилятора, возможно, также от архитектуры процессора и количества ядер.

Буфер cout можно рассматривать как очередь, поэтому Альнитак прав.

0 голосов
/ 22 марта 2009

Кроме того, парадигмы смешивания вывода (printf и cout) зависят от конкретной реализации.

...