【问题标题】:What are identifiers in C exactly?C 中的标识符到底是什么?
【发布时间】:2020-11-15 15:11:24
【问题描述】:

每个 Google 搜索都将它们解释为“变量的名称”,但我感觉标识符和标识符的名称之间存在区别。标识符是否更像具有名称、范围、链接和基础对象等属性的对象?我问这个是因为我在尝试通读 C 标准时遇到了一些麻烦。例如,sn-p

int main(){
  int x;
  extern int x;
}

编译失败而

int main(){
  int x;
  if(1){extern int x;}
}

编译成功。在this 问题中,第一个sn-p 的失败是从C 标准中的6.2.2.6 开始解释的,其中指出局部变量没有链接。但是,在第二个 sn-p 中,局部变量仍然没有链接,但没有冲突。现在,6.2.2.4 州

对于使用存储类说明符 extern 在该标识符的先前声明可见的范围内声明的标识符,如果先前声明指定内部或外部链接, 后面声明的标识符链接与前面指定的链接相同 宣言。如果前面的声明不可见,或者前面的声明没有指定链接,则 标识符有外部链接。

我的解释是该规则在两个 sn-ps 中都有效,但在第一个规则中,x 的底层对象的唯一性通过 6.2.1.2 触发了约束违规,因为相同的标识符名称是用于具有相同范围和名称空间的两个不同对象。但这不是我之前链接的问题的答案中给出的解释。在第二个 sn-p 中,链接类型仍然存在冲突,那么更改 extern 声明的范围是否会改变本地声明的可见性?从 C 标准的抽象角度考虑链接的最佳方式是什么(不使用 gcc 或 clang 等实际实现作为说明)?

【问题讨论】:

  • 6.2.2.6 只是说没有 extern 存储类说明符的本地标识符没有链接。
  • 链接是指范围内的名称。第二个示例编译,因为if 块范围内的名称x 与封闭main 主体中的x 不同。例如,请参阅this snippet
  • @dxiv 我试图弄清楚为什么 if 块中的声明根据 C 标准引用了不同的实体。为什么这个实体与在main 之外声明为static int x; 的实体不同?
  • @GuachoPerez 与if(1) {int x;} 掩盖外部x 的原因相同。如果您在文件级别添加static int x;,由于链接冲突,这将再次成为错误。
  • @dxiv 但在第二个 sn-p 中将 static int x; 添加到 main 之外可以正常编译。编译器如何知道extern int xint x 不同但与static int x 不同?

标签: c compilation linkage


【解决方案1】:

“标识符”是语言语法的一个元素。预处理后,所有标记为以下之一:keywordidentifierconstantstring-literal 或 标点符号。

如果标记以字母(或下划线)开头,则它只能是关键字或标识符。如果它不在关键字表中,则它是一个标识符。有关这方面的更多技术细节,请参阅 C 标准的附录 A。

在您的程序中,xmain 是标识符,intifextern 是关键字,1 是常量,其他所有内容都是标点符号。


标识符用作实体的名称。同一个标识符可以用在不同的范围内来指定不同的实体(或同一个实体)。 Linkage 是标识符与实体关联的过程的名称。

有时标准使用“标识符”一词来表示由标识符标识的实体,这在 6.2.1/5 中有所介绍:

除非另有明确说明,否则本国际标准使用术语“标识符”来指代某个实体(与句法构造相反),它指的是相关名称空间中的实体,其声明在该点可见标识符出现。


第一个代码是错误的,因为 6.7/3:

如果标识符没有链接,则标识符的声明(在声明符或类型说明符中)不得超过一个具有相同范围和相同名称空间的声明,除了:[...]

int x; 没有链接,因此在同一范围内不应有x 的另一个定义。 (例外列表与本案例无关)。

在第二个代码中,没有违反 6.7/3,因为第二个声明与第一个声明不在同一范围内。您引用的文字解释说,extern int x; 命名的实体与 int x; 命名的实体不同,这很好。

由于声明具有外部链接的标识符但未提供定义,第二个程序具有未定义的行为(无需诊断)。您可能会看到也可能不会看到错误消息。

【讨论】:

  • 谢谢你,我们真的很有帮助。我只有关于第二段到最后一段的问题。用户@dxiv 还提到,在第二个代码中,两个声明都引用了不同的实体。这是由于第二个声明根据 6.2.1.4 隐藏了第一个声明,因此,6.2.2.4 的“没有可见的先前定义”部分成立,迫使第二个标识符是外部的?我想这不是全貌,因为如果这是真的,那么 6.2.2.4 将永远不会使外部声明具有内部链接。
  • @GuachoPerez 在第二个代码中有一个可见的相同标识符的先前声明。这就是您在问题中引用的文字适用的原因。 x 的先前声明没有链接,x 的新声明具有外部链接并命名与第一个 x 不同的实体。
  • 那么当我在第二个代码中在main 之外添加static int x; 时,6.2.2.4 如何应用?本地声明仍然可见,因此if 块中的extern 将导致x 具有外部链接,而static 将使其具有内部链接。这会导致未定义的行为还是x 采用内部链接并等于在字段范围内声明的实体?
  • @GuachoPerez 还有另一个子句说如果标识符出现在同一个翻译单元中同时具有外部和内部链接,则行为未定义
  • @M.M 我承认我发现 6.2.2/4 的措辞相当混乱。 "对于使用存储类说明符 extern 声明的标识符...如果前面的声明指定 内部或 外部链接,则后面声明中标识符的链接 与前面声明中指定的链接相同"。这可以理解为允许static int x; 后跟extern int x;,而后者“继承”了前者的内部链接。虽然情况似乎并非如此。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-06
  • 2017-10-12
  • 1970-01-01
  • 1970-01-01
  • 2023-03-16
  • 1970-01-01
相关资源
最近更新 更多