视觉C++:在 DLL 加载期间,全局变量初始化顺序是否具有确定性?
Visual C++: Is global variables initialization order deterministic during DLL loading?
>假设我构建了一个简单的DLL,由两个翻译单元组成:
第一
// foo.cpp
struct Foo
{
//...
} g_foo;
// ... other stuff
第二个
// bar.cpp
struct Bar
{
//...
} g_bar;
// ... other stuff
我知道C++标准没有指定全局变量初始化的顺序。问题是:一旦我有了构建的 Windows DLL,在调用期间执行的全局变量初始化顺序是确定性的LoadLibrary
(每个LoadLibrary
调用都会以相同的顺序启动变量g_foo
和g_bar
的初始化(还是可能取决于某些加载器/系统设置?
C++标准没有指定顺序 全局变量初始化。 准确地说,当全局变量位于单个翻译单元中时,它会这样做:
有序动态初始化,适用于所有其他非本地 变量:在单个翻译单元中,初始化这些变量 变量总是按照其定义的精确顺序排序 出现在源代码中。
上面的 DLL 代码,在两个不同的翻译单元中有两个不同的全局变量,将导致两个不同的 .链接前的 OBJ 文件。然后,当 .OBJ 文件链接在一起形成一个.DLL,C++"pre-main"运行时代码将附加到.DLL。当.DLL通过进程启动或LoadLibrary
绑定到进程的地址空间时,此存根代码将有权访问 DLL 中的一个表,在调用 DllMain 之前,它将遍历、调用表中链接时间合成的静态非成员函数,每个非成员函数的任务是在表中为相应的全局对象运行类成员构造函数。当然,在从进程地址空间中删除 DLL 期间,无论是通过进程退出还是通过FreeLibrary
,非成员函数都将以类似的方式调用,但顺序相反 (LIFO(。
鉴于此表由 LINK.EXE "烘焙"到.DLL中,DLL 中全局变量的构造顺序(无论它们来自同一转换单元还是不同(都将预先确定。正如您所指出的,它在链接时间之前是不可预测的,但是无论它在链接时间之后变成什么,它都会在.DLL的生命周期中保留下来,因为只有 LINK.EXE 有能力构造该全局变量构造函数表,一旦构造它,它就会被构造。
如果有人想知道哪个先来:全局变量的构造,还是程序员提供的DllMain,那就是前者。C++运行时代码调用程序员提供的 DllMain,如 @Algirdas Preidžius 提供的链接所示。
- 检查 2 棵树是否具有相同的顺序
- 检查一个数组是否包含在另一个数组中,以相反的顺序,至少两次
- C++编译器是否优化了顺序静态变量读取?
- 同一互斥锁顺序上的锁定和解锁是否一致?
- C++评估顺序优化是否意味着对不同的操作数使用不同的内核?
- 标记为 std::memory_order_seq_cst 的单个原子操作是否会在所有位置触发顺序一致性?
- 如何检查参数包是否具有执行顺序中的确切类型
- 视觉C++:在 DLL 加载期间,全局变量初始化顺序是否具有确定性?
- c++标准是否指定了运算符&&(内置)的求值顺序?
- 顺序一致的原子负载(负载-负载对)是否形成线程间同步点
- 在C++中,是否可以在单个循环中顺序迭代多个迭代器
- 初始化值是否保证通过其自己的地址反映,而不考虑内存顺序
- 如果 RMW 操作没有任何变化,是否可以针对所有内存顺序对其进行优化
- 是否可以跨动态库控制销毁顺序?
- 验证(使用 static_assert)元组类型是否遵循某种顺序(有状态编译时检查)
- 委派的 ctor 是否受参数计算顺序的影响?
- 如何检查 2 个 c++ 数组在 O(1) 或 O(log n) 时间复杂度中是否相同(所有元素都相同,顺序很重要)?
- 是否有任何内置函数可以检查给定的两个数字在给定整数数组中的顺序是否相同?
- 编译器是否会简化按顺序执行多次的操作
- C++是否保证标准容器比较中操作数的顺序?