2

For the following code:

int main()
{
}

For g++ -pedantic -Wall -O3 -S -o test.asm test.cpp && cat test.asm, the assembly output is unsurprisingly clean. If I do gcc -x c ... test c with #include <stdio.h> at the top, the output is identical except for the label names. Actually it doesn't matter if it's in C++ or C mode. Other headers like <string>, <vector>, <algorithm>, <ostream> etc. give identical results.

    .file   "test.cpp"
    .section    .text.unlikely,"ax",@progbits
.LCOLDB0:
    .section    .text.startup,"ax",@progbits
.LHOTB0:
    .p2align 4,,15
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    xorl    %eax, %eax
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .section    .text.unlikely
.LCOLDE0:
    .section    .text.startup
.LHOTE0:
    .ident  "GCC: (GNU) 4.9.0 20131223 (experimental)"
    .section    .note.GNU-stack,"",@progbits

But as soon as you add #include <iostream>, it blows up (identical part omitted):

    .section    .text.unlikely
.LCOLDE0:
    .section    .text.startup
.LHOTE0:
    .section    .text.unlikely
.LCOLDB1:
    .section    .text.startup
.LHOTB1:
    .p2align 4,,15
    .type   _GLOBAL__sub_I_main, @function
_GLOBAL__sub_I_main:
.LFB1027:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $_ZStL8__ioinit, %edi
    call    _ZNSt8ios_base4InitC1Ev
    movl    $__dso_handle, %edx
    movl    $_ZStL8__ioinit, %esi
    movl    $_ZNSt8ios_base4InitD1Ev, %edi
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    jmp __cxa_atexit
    .cfi_endproc
.LFE1027:
    .size   _GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
    .section    .text.unlikely
.LCOLDE1:
    .section    .text.startup
.LHOTE1:
    .section    .init_array,"aw"
    .align 8
    .quad   _GLOBAL__sub_I_main
    .local  _ZStL8__ioinit
    .comm   _ZStL8__ioinit,1,1
    .hidden __dso_handle
    .ident  "GCC: (GNU) 4.9.0 20131223 (experimental)"
    .section    .note.GNU-stack,"",@progbits

What makes <iostream> special?

One quote that might be relevant is:

In terms of static initialization order, cout is guaranteed to be properly constructed and initialized no later than the first time an object of type ios_base::Init is constructed, with the inclusion of counting as at least one initialization of such objects with static duration.

Yu Hao
  • 119,891
  • 44
  • 235
  • 294

1 Answers1

2

This MSDN forum link provides a code snippet:

#include <ostream>
#include <istream>

namespace std
{
    extern istream cin;
    extern ostream cout;
    ....

    // this is explained below
    static ios_base::Init __foo;    // not its real name
} 

Relevant quote:

Now, the runtime penalty mentioned previously: the global objects must be initialized before any of your own code uses them; this is guaranteed by the standard. Like any other global object, they must be initialized once and only once. This is typically done with a construct like the one above, and the nested class ios_base::Init is specified in the standard for just this reason.

And the standard quote:

According to ยง27.3/2: "The objects [std::cin, std::cout, etc.] are constructed, and the associations are established at some time prior to or during first time an object of class ios_base::Init is constructed, and in any case before the body of main begins execution."

The culprit is indeed the ios_base::Init line, as cHao's comment suggests. So if the msdn link is correct, then that means that <iostream> is special for that reason.

Community
  • 1
  • 1