web-dev-qa-db-ja.com

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:とは何ですか?

37
schorsch312

<iostream>を含む各翻訳単位には、ios_base::Initオブジェクトのコピーが含まれます。

static ios_base::Init __ioinit;

このオブジェクトは、標準ストリーム(std::coutおよびそのフレンド)を初期化するために使用されます。このメソッドは Schwarz Counter と呼ばれ、標準ストリームが最初に使用される前に必ず初期化されるようにします(iostreamヘッダーが含まれている場合)。

その関数_GLOBAL__sub_I_mainは、翻訳ユニット内のグローバルオブジェクトのコンストラクターを呼び出す翻訳ユニットごとにコンパイラーが生成するコードであり、対応するデストラクター呼び出しが終了時に呼び出されるように調整します。このコードは、mainが呼び出される前にC++標準ライブラリの起動コードによって呼び出されます。

33

iostreamヘッダーを含めると、静的std::ios_base::Initオブジェクトの定義を追加する効果があります。この静的オブジェクトのコンストラクタは、標準ストリームオブジェクトstd::coutstd::cerrなどを初期化します。

これが行われる理由は、静的な初期化順序の大失敗を避けるためです。これにより、翻訳オブジェクト間でストリームオブジェクトが適切に初期化されます。

23
StoryTeller