TGViewer
Грокаем C++ Грокаем C++ @grokaemcpp · 9.35K subscribers
Post #480 6.44K
​​std::cout

Кажется, что на начальном этапе становления про-с++-ером, вывод в использование конструкции:

std::cout << "Print something in consol\n";


воспринимается, как "штука, которая выводит текст на консоль".

Даже со временем картинка не до конца складывается и на вопрос "что такое std::cout?", многие плывут. Сегодня закроем этот вопрос.

В этой строчке мы вызываем такой оператор:

std::ostream& operator<< (std::ostream& stream, const char * str)


Получается, что std::cout - объект класса std::ostream. И ни какой-то там временный. Раз он принимается по левой ссылке, значит он уже где-то хранится в памяти.

Но мы же ничего не делаем для его создания? Откуда он взялся?

Мы говорили о том, что есть "невидимые" для нас вещи, которые происходят при старте программы. Так вот, это одна из таких вещей.

std::cout - глобальный объект типа std::ostream. За его создание отвечает класс std::ios_base::Init, инстанс которого явно или неявно определяется в библиотеке <iostream>.

Но это все слова. И новичкам будет достаточно этого. Но мы тут глубоко закапываемся, поэтому давайте закопаемся в код.

Полазаем по исходникам gcc. Ссылочки кликабельные для пытливых умов.

А в хэдэре iostream мы можем найти вот это:

extern istream cin;  ///< Linked to standard input
extern ostream cout; ///< Linked to standard output
extern ostream cerr; ///< Linked to standard error (unbuffered)
extern ostream clog; ///< Linked to standard error (buffered)
...
static ios_base::Init __ioinit;


Здесь определяются символы стандартных потоков и создается глобальная переменная класса ios_base::Init. Пойдемте тогда в конструктор:

ios_base::Init::Init()
{
if (__gnu_cxx::__exchange_and_add_dispatch(&_S_refcount, 1) == 0)
{
// Standard streams default to synced with "C" operations.
_S_synced_with_stdio = true;

new (&buf_cout_sync) stdio_sync_filebuf<char>(stdout);
new (&buf_cin_sync) stdio_sync_filebuf<char>(stdin);
new (&buf_cerr_sync) stdio_sync_filebuf<char>(stderr);

// The standard streams are constructed once only and never
// destroyed.
new (&cout) ostream(&buf_cout_sync);
new (&cin) istream(&buf_cin_sync);
new (&cerr) ostream(&buf_cerr_sync);
new (&clog) ostream(&buf_cerr_sync);
cin.tie(&cout);
cerr.setf(ios_base::unitbuf);
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 455. cerr::tie() and wcerr::tie() are overspecified.
cerr.tie(&cout);
...
__gnu_cxx::__atomic_add_dispatch(&_S_refcount, 1);


Немножко разберем происходящее.

В условии проверяется ref_count, чтобы предотвратить повторную инициализацию. Так как не предполагается, что такие объекты, как cout будут удалены, они просто создаются через placement new с помощью инстансов stdio_sync_filebuf<char>. Это внутренний буфер для объектов потоков, который ассоциирован с "файлами" stdout, stdin, stderr. Буферы как раз и предназначены для получения/записи io данных.

Хорошо. Мы видим как и где создаются объекты. Но это же placement new. Для объектов уже должная быть подготовлена память для их размещения. Где же она?

В файлике globals_io.cc:

 // Standard stream objects.
// NB: Iff <iostream> is included, these definitions become wonky.
typedef char fake_istream[sizeof(istream)]
attribute ((aligned(alignof(istream))));
typedef char fake_ostream[sizeof(ostream)]
attribute ((aligned(alignof(ostream))));
fake_istream cin;
fake_ostream cout;
fake_ostream cerr;
fake_ostream clog;


то есть, объекты - это пустые символьные массивы правильного размера и выравнивания.

Все это должно вам дать довольно полное представление, что такое стандартные потоки ввода-вывода.

#cppcore #compiler
  • 👍 61
  • 🔥 17
  • ❤ 12
  • 🤯 6
More from @grokaemcpp
  1. Oct 14, 2026​​Что внутри std::condition_variable? #опытным Более менее все знакомы с примитивом синхро…
  2. Oct 12, 2026​​WAT #новичкам Как вы думаете, результат выражения: bool math = -1 < 1U; будет правдой ил…
  3. Oct 8, 2026​​Strict weak ordering #опытным На первый взгляд, всё выглядит рабочим: мы создаём 40 зака…
  4. Oct 7, 2026​​Где-то баг... #опытным Вот вам код: struct Order { int price; int id; }; int main() { st…
  5. Oct 5, 2026Откуда spurious wakeup на кондваре? #опытным У кондваров есть метод std::condition_variabl…
  6. Oct 1, 2026​​Stacktrace. Tips #опытным Чтобы полноценно работать со стандартными трейсами, нужно знат…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →