代码之家  ›  专栏  ›  技术社区  ›  adversarr

全局变量何时导出到可执行文件?

  •  4
  • adversarr  · 技术社区  · 9 月前

    假设我有一个包含两个文件的库(共享或静态): lib.h lib.cpp .

    lib。h :

    #pragma once
    
    struct Trigger {
      struct TriggerImpl{
        TriggerImpl();
      };
      inline static TriggerImpl trigger;
    };
    

    lib.cpp :

    #include "lib.h"
    #include <iostream>
    
    struct LogConsole {
      LogConsole() { std::cout << "LogConsole constructor" << std::endl; };
    };
    
    LogConsole log_console;
    
    Trigger::TriggerImpl::TriggerImpl() {
      std::cout << "Trigger constructor" << std::endl;
    }
    

    然后,我将库链接到可执行文件。

    main.cpp :

    #include <iostream>
    // #include "lib.h" ///< uncomment this line and you will get different result!!!
    
    int main() {
      std::cout << "Done." << std::endl;
      return 0;
    }
    

    使用静态链接(例如CMake target_link_library(... STATIC ...) ),您将获得:

    Done.
    

    使用动态链接(例如CMake target_link_library(... SHARED ...) ),您将获得:

    Trigger constructor
    LogConsole constructor
    Done.
    

    但是,如果您取消注释 #include 行,并使用静态链接,你会得到:

    触发器构造函数
    LogConsole构造函数
    完成。
    

    我的问题是:

    1. 为什么动态链接和静态链接会(或不会)调用全局变量的构造函数?

    2. 我能理解,包括头部将初始化 trigger ,但为什么会 log_console 也要初始化吗?编译器/链接器的内部机制是什么?

    可能的相关链接包括:

    1. Language Linkage

    2. Storage Duration

    但我认为这可能是某个特定工具链的“特征”(我使用的是: gcc 14.1.1 在我的x64桌面上使用Manjaro Linux)。

    1 回复  |  直到 9 月前
        1
  •  4
  •   wohlstad    9 月前

    两者皆有 trigger log_console 已初始化 if and only if lib.cpp 实际上与过程有关。

    当你建造 lib.cpp 将主可执行文件直接链接到共享库中,然后动态加载器将共享库加载到您的进程中并初始化它(在可执行文件的第一条指令运行之前)。

    当你建造 lib.cpp 在归档库中,会发生什么取决于静态链接器是否选择 lib.o 是否进入链接。

    链接器将仅选择 lib。o 进入链接 如果 这样做是有原因的。请参阅 this post 了解全部细节。

    当你 #include "lib.h" ,确实有这样的参考 main.o lib。o ,链接器拉 lib。o 进入主可执行文件。

    当你发表评论时 // #include "lib.h" ,有 没有什么 在里面 主要。o 这向链接器表明 lib。o 需要,因此它跳过该对象。

    您可以在实际操作中观察到这一点:

    • 具有 #include 评论出来, g++ main.cpp 将成功生成主二进制文件
    • 具有 #包括 在中注释后,同一命令将失败,无法解析 触发 .

    你也可以检查 nm main.o 对于这两种情况,请观察 #包括 有一个未解决的问题 触发 符号,并与它评论了没有。

    附言:链接者只能决定是否链接 lib。o 是否进入二进制文件,如果 lib。o 在档案馆里。如果 lib。o lib.cpp 如果直接在命令行上列出,则链接器没有选择权 必须 包括 lib。o (这就是为什么塞尔比的实验未能再现观察到的行为)。