【问题标题】:Clarification on Scope and Redefinition [duplicate]关于范围和重新定义的澄清[重复]
【发布时间】:2014-04-16 12:08:05
【问题描述】:

参考下面的代码:

#include <stdio.h>

int a;
int a;

int main()
{
    int b;
    int b;

    return 0;
}

为什么编译器 (GCC) 抱怨只重新声明变量“b”而不是“a”?

redef.c:在函数'main'中: redef.c:19:错误:重新声明“b”而没有链接

redef.c:18: 错误:'b' 的先前声明在这里

【问题讨论】:

标签: c


【解决方案1】:

这是因为a 具有外部链接和标准状态(C11,6.2.2/2):

在不同范围或同一范围内多次声明的标识符可以通过称为链接的过程引用同一对象或函数。链接分为三种:外部、内部和无。

在构成整个程序的翻译单元和库集合中,具有外部链接的特定标识符的每个声明都表示相同的对象或函数。在一个翻译单元中,具有内部链接的标识符的每个声明都表示相同的对象或函数。 每个没有链接的标识符声明都表示一个唯一的实体。

所以,因为a 具有外部链接,所以这两个声明都引用了相同 基础变量。因为b 没有链接,所以声明引用了唯一变量,因此相互冲突。

【讨论】:

  • 那么两个语句中的哪一个定义了变量a——第一个?
  • 我认为这句话并不能确定 OP 代码的合法性或非法性。它只是意味着如果代码是合法的,那么两个标识符都表示同一个对象。
  • @Matt,“如果你做 A,就意味着 B”这句话非常确定了 A 的合法性,前提是 B 不是“编译器会产生错误”。
【解决方案2】:

引用 C99 标准 §6.9.2 ¶2

具有文件范围的对象的标识符声明 没有初始化器,也没有存储类说明符或 存储类说明符静态,构成一个暂定的 定义。如果翻译单元包含一个或多个暂定 标识符的定义,并且翻译单元不包含 该标识符的外部定义,则行为正是 好像翻译单元包含该文件的文件范围声明 标识符,与翻译结束时的复合类型 单位,初始化器等于 0。

因此,这两种说法

int a;
int a;

构成暂定定义。根据上面引用的部分,行为就像 这两个语句被替换为

int a = 0;

但是,在main 中定义的b 是一个自动变量,即它具有自动存储分配。一个自动变量不能有两个定义。

【讨论】:

    【解决方案3】:

    int a; int a; 是一个暂定定义。从 6.9.2p2 开始:

    具有文件范围但没有初始化器的对象的标识符声明,以及 没有存储类说明符或带有存储类说明符 static,构成一个 暂定定义。如果一个翻译单元包含一个或多个暂定定义 标识符,并且翻译单元不包含该标识符的外部定义,则 该行为就像翻译单元包含该文件的文件范围声明一样 标识符,具有从翻译单元末尾开始的复合类型,带有初始化器 等于 0。

    暂定定义只允许在文件范围内。

    int b; int b;非法的原因是因为6.7p3:

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

    在函数中声明的标识符而不是 staticextern没有链接,这在 6.2.2p6 中有描述

    以下标识符没有链接:声明为任何其他标识符的标识符 一个对象或一个函数;声明为函数参数的标识符;块作用域 未使用存储类说明符 extern 声明的对象的标识符。

    【讨论】:

      猜你喜欢
      • 2013-06-12
      • 1970-01-01
      • 2014-04-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-14
      • 2017-12-29
      • 1970-01-01
      相关资源
      最近更新 更多