1

假设我构建了一个简单的 DLL,由两个翻译单元组成:

第一的

// foo.cpp
struct Foo
{
  //...
} g_foo;
// ... other stuff

第二个

// bar.cpp
struct Bar
{
  //...
} g_bar;
// ... other stuff

我知道 C++ 标准没有指定全局变量初始化的顺序这一事实。问题是:一旦我构建了 Windows DLL,在LoadLibrary调用期间执行的全局变量初始化的顺序是确定性的(每次LoadLibrary调用都会以相同的顺序启动变量的初始化g_foog_bar还是可能取决于某些加载器/系统设置?

4

1 回答 1

1

我知道 C++ 标准没有指定全局变量初始化的顺序这一事实。准确地说,当全局变量位于单个翻译单元中时,它会这样做:

有序动态初始化,适用于所有其他非局部变量:在单个翻译单元中,这些变量的初始化始终按照它们在源代码中的定义出现的确切顺序进行排序。

上面的 DLL 代码在两个不同的翻译单元中有两个不同的全局变量,在链接之前将产生两个不同的 .OBJ 文件。然后,当 .OBJ 文件链接在一起形成 .DLL 时,C++“pre-main”运行时代码将附加到 .DLL。当 .DLL 通过 process-launch 或 by 绑定到进程的地址空间时LoadLibrary,此存根代码将有权访问 DLL 中的表,在调用 DllMain 之前,它将遍历,调用链接时间-在表中合成静态非成员函数,每个非成员函数的任务是在表中为相应的全局对象运行类成员构造函数。自然地,在从进程地址空间中删除 DLL 期间,通过进程退出或通过FreeLibrary,非成员函数将被类似地调用,但以相反的(LIFO)顺序。

鉴于此表是由 LINK.EXE “烘焙”到 .DLL 中的,DLL 中全局变量的构造顺序,无论它们来自相同的翻译单元还是不同的翻译单元,都将是预先确定的。正如您所指出的,在链接时间之前它是不可预测的,但是在链接时间之后它会变成什么将在 .DLL 的生命周期内保持不变,因为只有 LINK.EXE 能够构造该全局变量构造函数表,一旦构建,它就被构建。

如果有人想知道哪个先出现:全局变量的构造,还是程序员提供的 DllMain,那就是前者。C++ 运行时代码调用程序员提供的 DllMain,如@Algirdas Preidžius 提供的链接所示

于 2021-12-11T21:30:28.003 回答