Вопрос
Я знаю, что у cout есть buffer несколько дней назад, и когда я гуглю это, там сказано, что буфер чем-то похож на стек, и получаю выходные данные 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), так что "Это функция, которая позволяет вам объединять выходные данные с помощью insertion".Но путь слева направо не может объяснить cout<
Тогда Алнитак скажи мне это, "Тот << operator - это действительно ostream & operator<<(ostream & os, int), так что другой способ написать это:оператор<< ( оператор<< ( оператор<< ( cout, a ), b ), c )",
Если сначала вычисляется самый правильный аргумент, это может быть немного объяснено.
Теперь я запутался в том, как работает буфер cout, кто-нибудь может мне помочь?
Решение
Вы смешиваете множество вещей.На сегодняшний день:
- Детали реализации
cout
- Связанные вызовы
- Соглашения о вызовах
Попробуйте прочитать о них по отдельности.И не думайте обо всех них сразу.
printf("i=%d i++=%d i--=%d " , i , i++ ,i-- );
Приведенная выше строка вызывает неопределенное поведение.Прочитайте часто задаваемые вопросы 3.2.Обратите внимание, то, что вы наблюдаете, является побочным эффектом соглашения о вызове функции и способа передачи параметров в стеке конкретной реализацией (т.е.твой).Это не гарантирует, что будет таким же, если вы работали на других машинах.
Я думаю, вы путаете порядок вызовов функций с буферизацией.Когда у вас есть cout
оператор, за которым следует несколько вставок <<
на самом деле вы вызываете несколько вызовов функций, один за другим.Итак, если бы вы написали:
cout << 42 << 0;
Это действительно означает:Ты звонишь,
cout = operator<<(cout, 42)
а затем используйте return в другом вызове того же оператора, что и:
cout = operator<<(cout, 0)
То, что вы проверили вышеизложенным, вам ничего не скажет cout
внутреннее представительство.Я предлагаю вам взглянуть на заголовочные файлы, чтобы узнать больше.
Другие советы
Просто в качестве общего совета, никогда не используйте i ++ в той же строке, что и другое использование i или i--.
Проблема в том, что аргументы функции могут быть вычислены в любом порядке, поэтому, если аргументы вашей функции имеют какие-либо побочные эффекты (такие как операции увеличения и уменьшения), вы не можете гарантировать, что они будут работать в том порядке, который вы ожидаете.Это то, чего следует избегать.
То же самое относится и к этому случаю, который аналогичен фактическому расширению использования вашего cout:
функция 1 ( функция 2 ( foo ), bar );
Компилятор может свободно удалять bar перед вызовом function2 или наоборот.Вы можете гарантировать, что функция 2 вернется, например, до вызова функции 1, но не то, что их аргументы будут вычислены в определенном порядке.
Это становится проблемой, когда вы делаете что-то вроде:
функция 1 ( функция 2 ( i++), i );
У вас нет способа указать, вычисляется ли "i" до или после "i ++", поэтому вы, скорее всего, получите результаты, отличные от ожидаемых, или разные результаты с разными компиляторами или даже разными версиями одного и того же компилятора.
Итог, избегайте заявлений с побочными эффектами.Используйте их только в том случае, если они являются единственным оператором в строке или если вы знаете, что изменяете одну и ту же переменную только один раз.("Строка" означает один оператор плюс точка с запятой.)
То, что вы видите, является неопределенным поведением.
Местные новости i
и глобальный c
добавляются / вычитаются несколько раз без точки последовательности.Это означает, что значения, которые вы получаете, могут быть о чем угодно.Зависит от компилятора, возможно, также архитектуры процессора и количества ядер.
В cout
буфер можно рассматривать как очередь, так что Алнитак прав.
В дополнение к другим ответам, которые правильно указывают на то, что вы видите неопределенное поведение, я решил упомянуть, что std::cout
использует объект типа std::streambuf
чтобы выполнить его внутреннюю буферизацию.По сути, это абстрактный класс, который представляет собой буфер (размер зависит от конкретной реализации и может даже быть равен 0 для небуферизованных потоковых буферов).Тот, который для std::cout
написан таким образом, что когда он "переполняется", он сбрасывается в стандартный вывод.
На самом деле, вы можете изменить std::streambuf
связанный с std::cout
(или любой поток, если уж на то пошло).Это часто полезно, если вы хотите сделать что-то умное, например, сделать все std::cout
вызовы заканчиваются в файле журнала или что-то в этом роде.
И как дерк джентли сказал вы путаете соглашение о вызовах с другими деталями, они совершенно не связаны с буферизацией std::cout .
Кроме того, парадигмы смешивания выходных данных (printf и cout) зависят от конкретной реализации.