【问题标题】:What is the formal way to determine the linkage of a `_Thread_local` identifier?确定 `_Thread_local` 标识符的链接的正式方法是什么?
【发布时间】:2018-07-07 01:05:25
【问题描述】:

这是一个关于 C11 标准中极端案例的“语言律师”类型的问题。

在 C 程序中确定标识符链接的规则在 C11 标准第 6.2.2 条。特别是,6.2.2(5) 指出(强调我的):

如果函数的标识符声明没有存储类说明符,则其链接 就像使用存储类说明符 extern 声明一样确定。如果 对象的标识符声明具有文件范围没有存储类说明符, 它的链接是外部的。

在文件范围内声明如_Thread_local int a;的情况下,上述6.2.2(5)不适用, 因为_Thread_local 一个存储说明符。 6.2.2 的其他规定均不适用 (没有static 所以(3)不适用,不在块范围内,是对象而不是参数,所以(6)不适用,等等)。所以呢 是否应该a 的链接符合标准?在这种情况下,我是否遗漏了其他一些确定链接的规则?

我知道这样做的目的是使其具有外部链接(这就是 gcc 处理这种情况的方式)但是如何 这是否遵循标准本身?

请注意,有这样的声明是完全可以的 static _Thread_local int a; extern _Thread_local int a; 在这种情况下,6.2.2 规则适用,使 a 具有内部链接(尽管有 extern)。

最后,_Thread_local 的语义在这里不相关。

【问题讨论】:

  • 您可能会注意到 C11 §6.7.1 Storage-class specifiers ¶3 不适用,因为声明是在文件范围内,而不是在块范围内。
  • 在 C99 中,§6.2.2 ¶5 说:如果函数标识符的声明没有存储类说明符,则它的链接完全确定,就好像它是用存储类说明符extern。如果对象标识符的声明具有文件范围且没有存储类说明符,则它的链接是外部的。 如果不是逐字副本,则本质上是相同的。我认为但不确定该段落是否适用,就好像_Thread_local 不存在一样。也许有人会说,如果从头开始,这里有一个小故障是可以避免的。
  • @Jonathan Leffler 完全同意 6.7.1 和(可能的)故障。另一个奇怪的案例是register 函数声明(当然,这没有任何意义):必须将 6.7.1(7) (禁止除 extern 之外的存储说明符用于块级函数)和 6.9( 2)(没有文件范围声明的寄存器)派生不能声明函数register。它有效,但我不确定原因是否如此复杂。
  • 关于函数,这很简单。 C11 §6.9.1 Function definitions ¶4 说:声明说明符中的存储类说明符(如果有)应为externstatic
  • _Thread_local 是第一个与所有其他不互斥的存储类说明符,也是自 C89 以来添加的第一个;所以我同意 Jonathan Leffler 的观点,认为这是一个编辑错误——他们错过了与“无存储类说明符”语言的交互。

标签: c language-lawyer c11 linkage thread-local-storage


【解决方案1】:

根据 C11 6.2.4p4,在文件范围内,_Thread_local 应用于 main() 的线程,有效地将它们作为静态或外部引用移动到 main() 的 { } 块中,而不是 auto,并且左实现定义了除 main() 之外的线程函数如何访问它们。我同意它似乎是一个缺陷,它没有提到任何 _Thread_local 对象的定义声明都应该被要求具有 static 或 extern 作为附加限定符,而不仅仅是“可能出现”作为引用声明,在 6.7.1p2 .

【讨论】:

  • 这似乎是一个缺陷的共识。有人可能会争辩说 'storage specifier' 是一个有点太宽泛的类,因为 autoexternstatic 有非常不同的用途:第一个是指定持续时间,第二个是链接,而第三个是一个都可以。然而,问题是关于linkage,所以 6.2.4 不是很相关。当然,main 以外的函数可以访问文件范围内声明的 _Thread_local 变量。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-08-31
  • 1970-01-01
  • 1970-01-01
  • 2011-04-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多