Поток (stream) — абстракция источника или приёмника данных. Стандартные потоки объявлены в <iostream>: std::cin — ввод, std::cout — вывод, std::cerr — вывод ошибок.

Не из лекции

На занятии разбирались std::cin, std::cout, цепочки >> и <<, '\n' и std::boolalpha. Сравнение с std::endl, std::cerr, std::getline и проверка состояния потока — материал не оттуда.

#include <iostream>
 
int main() {
  int value = 0;
  std::cin >> value;
  std::cout << "Получено: " << value << '\n';
  return 0;
}

Операторы >> и << можно выстраивать цепочкой, потому что каждый возвращает сам поток: std::cin >> a >> b читает два значения подряд.

Если программа ждёт ввод, отсутствие нового вывода ещё не означает, что она зависла.

В std::cout << x символы << означают вывод, а не сдвиг: смысл операции определяют операнды, а не значок. См. Побитовые операции.

Логические значения на выводе

По умолчанию bool печатается числом. std::boolalpha меняет формат на словесный:

std::cout << std::boolalpha;
std::cout << (0.1 + 0.2 == 0.3) << '\n';   // false вместо 0

Манипулятор липкий — действует до отмены (std::noboolalpha) и меняет только формат вывода, а не сами значения.

Как читается ввод

>> пропускает ведущие пробельные символы (пробелы, табуляции, переводы строк), читает столько, сколько подходит под тип, и останавливается на первом неподходящем символе, оставляя его в потоке.

Отсюда следствия:

  • разделение чисел пробелами или переводами строк для >> неразличимо;
  • чтение строки через >> даёт одно слово, а не всю строку — для строки целиком есть std::getline;
  • если во вводе не число, чтение не выполняется, поток переходит в состояние ошибки и переменная остаётся с прежним значением. Именно поэтому её важно инициализировать заранее.

Проверить успешность можно прямо в условии — поток приводится к bool:

if (!(std::cin >> value)) {
  // ввод не удался
}

'\n' или std::endl

Оба переводят строку, но std::endl дополнительно сбрасывает буфер вывода (flush). Сброс — обращение к операционной системе; в цикле на сотни тысяч итераций разница заметна и решает задачу по времени.

std::cout << value << '\n';        // просто перевод строки
std::cout << value << std::endl;   // перевод строки + сброс буфера

По умолчанию используют '\n'. std::endl нужен, когда вывод должен появиться немедленно: перед долгой операцией, при отладке падающей программы.

Одиночный символ пишут в одинарных кавычках: '\n' — это char, а "\n" — строка из двух байт, включая завершающий ноль.

Тонкости

  • Вывод буферизуется. Если программа падает, часть вывода может не появиться — при отладке печатью это сбивает с толку, и здесь std::endl уместен.
  • std::cerr не буферизуется и не смешивается с std::cout при перенаправлении в файл — для сообщений об ошибках используют его.
  • Форматирование (ширина поля, заполнитель, точность) живёт в <iomanip>, см. Форматирование вывода.
  • В соревновательных задачах ускоряют ввод-вывод отключением синхронизации с C-потоками; в учебных задачах это не нужно.

Частые ошибки

  • std::endl в цикле на большом объёме вывода — потеря времени на ровном месте.
  • Чтение строки с пробелами через >> — получится только первое слово.
  • Отсутствие проверки, что ввод вообще прочитался.