【问题标题】:Is order of static initialization implementation defined?是否定义了静态初始化实现的顺序?
【发布时间】:2014-07-02 12:34:36
【问题描述】:

动态初始化可以有序也可以无序:

使用静态存储动态初始化非局部变量 持续时间是有序的或无序的。

这并没有说明静态初始化的顺序。静态初始化的顺序是实现定义的吗?

【问题讨论】:

  • 函数范围内的静态在第一次使用时初始化。在文件范围内(在 main 之前),文件的静态从上到下初始化。对于文件中的静态文件,未指定顺序。
  • @MohitJain 请提供对描述该标准的参考。
  • 我已将其添加为详细答案。
  • 您混淆了静态初始化静态存储持续时间对象的初始化。它们不是一回事。
  • 静态初始化包括零初始化和常量初始化。我不认为你可以得到任何一个的订单依赖。

标签: c++ language-lawyer static-initialization


【解决方案1】:

静态初始化不是的意思是“用静态存储持续时间初始化变量”。这是一个更有限的术语,在 §3.6.2 [basic.start.init]/p2 中定义。

零初始化和常量初始化合称为静态初始化;所有其他初始化都是动态初始化。静态初始化应在任何动态初始化发生之前执行。

常量初始化在同一段中定义,基本上涉及到编译时常量的初始化。

因为静态初始化涉及到编译时常量的初始化,并且保证在任何动态初始化发生之前发生,所以顺序并不重要。 (实际上,对于静态存储时长的对象,初始值可能放在编译后的可执行文件的数据段(用于零初始化的bss段)中,并在程序启动时由操作系统直接加载,因此可以说是“顺序” 没有多大意义。)任何可能出现“静态初始化惨败”的事情实际上都涉及到动态初始化。

【讨论】:

  • 您对所讨论的错误术语是正确的,但根据 OP 的引述,他正在谈论 具有静态存储的非局部变量。然后 IMO 静态初始化 [非静态变量] 不是他所要求的(静态和非静态的规则不同)。
  • @AdrianoRepetti 他引用了题为“非局部变量的初始化”的部分。所有与这些变量的动态初始化相关的排序规则立即出现在他引用的那句话之后,我认为没有必要重复。
  • 不,我不同意你的最后一段。根据 §3.6.2 第 1 和 2 段。
  • @AdrianoRepetti 你不同意什么?
  • @DmitryFucintv 您可以查看平台可执行文件格式的规范。对于 Linux,它是 ELF。
【解决方案2】:

一般关于使用静态存储的变量的动态初始化

  • 函数范围内的静态对象在第一次使用时被初始化(只是 访问前)。
  • 在文件范围内(非本地范围,在 main 之前),文件的静态对象按定义顺序初始化
  • 对于位于文件(编译单元)中的文件范围静态对象,未指定顺序。

如果您想了解更多详情,请阅读以下内容:

引用cppreference

在块范围内声明的静态变量首先被初始化 时间控制通过他们的声明(除非他们的 初始化是零初始化或常量初始化,可以是 在第一次进入块之前执行)。在所有进一步的电话中, 声明被跳过。

引自 6.7 声明语句 stmt.dcl (n3690)

所有块范围变量的零初始化(8.5)静态 存储持续时间(3.7.1)或线程存储持续时间(3.7.2)是 在任何其他初始化发生之前执行。持续的 具有静态存储的块范围实体的初始化(3.6.2) 持续时间,如果适用,在它的块是第一个之前执行 输入。

引用 来自 3.6.2 basic.start.init (n3690)

其他具有静态存储持续时间的非局部变量已排序 初始化。具有有序初始化的变量定义在一个 单个翻译单元应按其顺序初始化 翻译单元中的定义。 如果一个程序启动一个线程(30.3),随后的初始化 变量相对于 a 的初始化是无序的 在不同的翻译单元中定义的变量。否则,该 变量的初始化是不确定的 在不同的翻译中定义的变量的初始化 单位。

您可能还想从 parashift 读取this 页面。

关于静态(const)初始化,它尽可能早地发生,最有可能在编译时发生。所有静态初始化都在动态初始化之前完成。

【讨论】:

  • 谢谢。但我正在从最终工作草案中寻找严格的规范参考(我使用的是 N3797)。我可以找到动态存储时长的初始化顺序(在 3.6.2/2 中指定),但不能找到静态初始化。
  • @DmitryFucintv 我应该在回答时添加这个。无论如何,已更正。
  • 通过文件范围静态变量,我的意思是非本地静态变量。即使没有在任何地方使用,它们也会在 main 之前初始化。
  • @MohitJain 通过文件范围静态变量,您可能是指命名空间范围(尽管类范围静态变量的行为方式大致相同)。
  • @MohitJain 关键句是“有副作用”(...如果初始化程序有副作用,它们总是被初始化(甚至在第一次使用之前)...) .如果(且仅当)没有副作用的实现可能(根据 3.6.2)在首次使用时推迟初始化。如果在代码中从未使用过没有副作用的初始化,那么是的...编译器甚至可以将其删除(作为优化)。
【解决方案3】:

静态初始化的顺序是实现定义的吗?

我看到很困惑的答案,所以让我总结一下:不,静态初始化顺序未定义。

让我们看看为什么以及在哪些情况下。根据您的报价,我假设您正在询问非局部变量静态存储持续时间的(静态或动态)初始化。

C++ standard§9.4.2(第 6 段)说:

静态数据成员的初始化和销毁​​与非局部变量完全一样。

那么根据 §3.6.2§3.7.1 不,编译单元内的 [order] 不是实现定义的,但它始终遵循声明顺序(如在 §6.74 段中描述):

在单个翻译单元中定义的具有有序初始化的变量应按照它们在翻译单元中的定义顺序进行初始化。

静态和动态初始化的规则是相同的,它们在同一段落中进行了描述,静态和动态初始化之间唯一(显着)的区别是排序,静态初始化总是在动态初始化之前发生,而动态初始化可能是无序的。

零初始化和常量初始化合称为静态初始化;所有其他初始化都是动态初始化。静态初始化应在任何动态初始化发生之前执行。

如何初始化静态初始化的非局部变量在 §3.6.212 段中描述:

具有静态存储持续时间 (3.7.1) 或线程存储持续时间 (3.7.2) 的变量应在任何其他初始化发生之前进行零初始化 (8.5)。执行常量初始化...[省略]

根据 cmets 我需要澄清这一点:非局部静态变量可能具有线程存储持续时间说明符 thread_local(然后它的行为就像来自用户 POV 的 static 变量坚果它们也可以组合在一起)。它仍然有资格进行静态初始化(然后排序,然后必须遵守上述规则),但不能简单地从磁盘加载数据段来执行初始化。必须执行代码(在 Windows 上为TlsAlloc),但从标准 POV 来看,它仍然是一个静态初始化表达式(首先零初始化,然后进行常量初始化)。让我们看一个非常虚构的例子:

thread_local unsigned int _value = 1;

现在让我们试着想象一下编译器是如何实现的。我们可以从已知的东西开始,想象类似于boost::thread_specific_ptr 的行为。在 Windows 上的实现(但在 Windows 上也非常相似)需要在线程初始化时调用 TlsAlloc,在线程完成时调用 TlsFree。您可以正常访问变量,但它可能会实现为一个指针,该指针与TlsAlloc 分配的内存有一个偏移量。唯一来自磁盘上的可执行文件的是初始值(同样,它只是一个你不应该关心的实现细节)。初始值(如前所述)将为 0(对于unsigned int)。鉴于所有这些代码、这些函数调用和如此多的细节......可能它仍然有资格被视为常量初始化,因为满足标准强加的要求,然后编译器应该(可能)尊重它们。这意味着它可能是静态初始化的。总结一下:

  • 标准静态初始化变量将遵守声明顺序。
  • 标准说明变量何时动态初始化和何时静态初始化。
  • 标准没有说明这些值在运行时应该来自哪里。
  • 标准没有说明实现应该如何初始化它们。

当然,在单个函数中同样的规则适用,但 declaration 应该替换为 usage (§6.7 ) 但这不是你的问题。

不授予不同编译单元的顺序,并且定义了它的实现(但有一些技术可以使这个顺序任意然后可预测:Nifty Counter 和变量聚合,例如)。

【讨论】:

  • §3.6.2§3.7.1 指非局部静态变量,而 §6.7 指块范围的静态变量。这些不是一回事,因此它们有不同的规则。具体来说,允许应用程序使用 §3.6.2 中的规则进行块范围静态变量的早期初始化,但也可以推迟到控制通过其声明。
  • @Edward 是的,3.6.2 和 3.7.1 用于非本地(例如编译单元,但根据 9.4.2 也引用了静态成员)。无论如何,我没有看到 permitted 块范围的一般规则在 6.7(和链接)中。
【解决方案4】:
  • 在同一个编译单元中,顺序是明确定义的(即,它遵循定义的顺序)。

  • 未指定不同编译单元的顺序。这是因为这个问题是在链接器级别而不是编译器级别解决的。

  • 这种歧义可以用已知的static initialization fiasco来区分。

【讨论】:

  • 您能否提供规范参考描述点12
  • @DmitryFucintv 是的,正在寻找它。
  • 关于第 2 点:不是未定义,未指定。在 C++ 标准中,undefined 有很特殊的含义,这里不适用。
猜你喜欢
  • 2021-07-12
  • 1970-01-01
  • 1970-01-01
  • 2010-09-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多