Включение iostream приводит к разным двоичным

Компиляция следующего кода

int main() {
    return 0;
}

дает сборку

main:
        xorl    %eax, %eax
        ret

https://gcc.godbolt.org/z/oQvRDd

Если теперь включен iostream

#include <iostream>   
int main() {
    return 0;
}

эта сборка создана.

main:
        xorl    %eax, %eax
        ret
_GLOBAL__sub_I_main:
        subq    $8, %rsp
        movl    $_ZStL8__ioinit, %edi
        call    std::ios_base::Init::Init() [complete object constructor]
        movl    $__dso_handle, %edx
        movl    $_ZStL8__ioinit, %esi
        movl    $_ZNSt8ios_base4InitD1Ev, %edi
        addq    $8, %rsp
        jmp     __cxa_atexit

Включена полная оптимизация (-O3). https://gcc.godbolt.org/z/EtrEX8

Может кто-то объяснить, почему включение неиспользуемого заголовка изменяет двоичный файл. Что такое _GLOBAL__sub_I_main:

Ответ 1

Каждая единица перевода, включающая <iostream> содержит копию объекта ios_base::Init:

static ios_base::Init __ioinit;

Этот объект используется для инициализации стандартных потоков (std::cout и его друзей). Этот метод называется Schwarz Counter, и он гарантирует, что стандартные потоки всегда инициализируются до их первого использования (при условии, что заголовок iostream включен).

Эта функция _GLOBAL__sub_I_main - это код, который компилятор генерирует для каждой единицы перевода, которая вызывает конструкторы глобальных объектов в этой единицы перевода, а также организует вызовы соответствующего деструктора, которые будут вызываться при выходе. Этот код вызывается кодом запуска стандартной библиотеки C++ до вызова main.

Ответ 2

Включение заголовка iostream приводит к добавлению определения статического объекта std::ios_base::Init. Конструктор этого статического объекта инициализирует стандартные потоковые объекты std::cout, std::cerr и т.д.

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