包含iostream导致不同的二进制

sch*_*312 37 c++ assembly iostream

编译以下代码

int main() {
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

给大会

main:
        xorl    %eax, %eax
        ret
Run Code Online (Sandbox Code Playgroud)

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

如果现在iostream包括在内

#include <iostream>   
int main() {
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这个程序集已创建.

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
Run Code Online (Sandbox Code Playgroud)

打开完全优化(-O3). https://gcc.godbolt.org/z/EtrEX8

有人可以解释一下,为什么包含一个未使用的头更改二进制 什么是_GLOBAL__sub_I_main:

Max*_*kin 33

每个<iostream>包含的翻译单元都包含一个ios_base::Init对象的副本:

static ios_base::Init __ioinit;
Run Code Online (Sandbox Code Playgroud)

此对象用于初始化标准流(std::cout及其朋友).此方法称为Schwarz Counter,它确保标准流在首次使用之前始终初始化(iostream包括提供的标头).

该函数_GLOBAL__sub_I_main是编译器为每个转换单元生成的代码,该转换单元调用该转换单元中的全局对象的构造函数,并且还安排在退出时调用相应的析构函数调用.调用此代码之前,C++标准库启动代码将调用此代码main.

  • 这就是为什么你应该在不需要引用cin,cout,cerr,wcin,wcout和wcerr的翻译单元中包含`<istream>`和/或`<ostream>`而不是`<iostream>`. (5认同)
  • 初始化,并且(可能更重要的是)在正常终止之前刷新它们. (3认同)

Sto*_*ica 23

包括iostream头部具有添加静态std::ios_base::Init对象的定义的效果.此静态对象的构造函数初始化标准流对象std::cout,std::cerr依此类推.

它完成的原因是避免静态初始化命令惨败.它确保流对象在翻译单元之间正确初始化.

  • 即使使用链接时优化,我也不认为编译器有足够的信息来理解`__ioinit`在不引用任何标准流的转换单元中是多余的. (4认同)