【问题标题】:Doubt related to extern keyword usage与extern关键字使用有关的疑问
【发布时间】:2010-02-27 19:46:58
【问题描述】:

AFAIK,extern 关键字应该用于声明,并且没有值可以与使用 extern 关键字声明的变量相关联。但是假设我写了这样的声明

extern int i = 10;

编译器是否应该标记相同的错误?我已经看到一些编译器容忍并忽略了这一点?为什么会这样? 'C' 标准对此有何规定?

编辑:@All,感谢您的回答。不过,我仍有疑问。假设我有这个变量的定义,而另一个文件中没有外部链接,比如 a.c,我在 b.c 中添加了这个语句。编译器仍然可以不标记错误吗?它会被重新定义吗?

【问题讨论】:

  • 不,因为这实际上是定义而不是声明。
  • LLVM 抛出警告:“extern variable has an initializer”

标签: c extern


【解决方案1】:

这是有效的语法,在 C99 标准中甚至有一个基本相同的示例。 (参见 §6.9.2-4。)

这些示例确实不规范,但我相信它的目的是合法的语法。编译器通常会输出警告,因为它并没有真正完成任何事情。

4 示例 1

int i1 = 1;             // definition, external linkage
static int i2 = 2;      // definition, internal linkage
extern int i3 = 3;      // definition, external linkage
int i4;                 // tentative definition, external linkage
static int i5;          // tentative definition, internal linkage
int i1;                 // valid tentative definition, refers to previous
int i2;                 // 6.2.2 renders undefined, linkage disagreement
int i3;                 // valid tentative definition, refers to previous
int i4;                 // valid tentative definition, refers to previous
int i5;                 // 6.2.2 renders undefined, linkage disagreement
extern int i1;          // refers to previous, whose linkage is external
extern int i2;          // refers to previous, whose linkage is internal
extern int i3;          // refers to previous, whose linkage is external
extern int i4;          // refers to previous, whose linkage is external
extern int i5;          // refers to previous, whose linkage is internal

【讨论】:

  • 如果它真的没有完成任何事情,为什么它是允许的?
  • 许多关键字和语法恰好是任何给定位置的默认解释。例如,函数内部int i;auto int i; 是相同的定义。虽然添加 auto 并没有完成任何特定的操作,但允许它提供对存储类说明符的一致使用。
【解决方案2】:

以下代码;

extern int i ;

声明一个变量 i,但不实例化它。如果它没有在同一个编译单元中定义,链接器将尝试从构成最终可执行文件的目标文件和库中解析它。

但是你的例子:

extern int i = 10 ;

初始化对象,因此也必须实例化它。在这种情况下,extern 关键字是多余的,因为对象是在同一个编译单元中初始化的(实际上是同一个语句)。相当于:

extern int i ;  // redundant
int i = 10 ;

虽然在最后一个示例中 extern 关键字是多余的,但它完全等同于在头文件中声明全局变量并在头文件中实例化一个源文件,它也包含该头文件(应该允许编译器执行类型检查)。

您可以按如下方式进行测试:

extern int i ;
int main()
{
    i = 10 ;
}

以上将导致未解析变量 i 的链接器错误。鉴于:

extern int i = 10 ;
int main()
{
    i = 10 ;
}

链接没有问题。

【讨论】:

    【解决方案3】:

    extern 关键字表示给定变量分配在不同的模块中。它与该变量的 access 无关。赋值给外部变量是完全合法的。

    【讨论】:

    • 首先,OP中的变量分配在this模块中,因为OP中的声明实际上是一个定义extern 并没有说某些东西“在不同的模块中”。它只是提供了一些外部联系。其次,OP中没有任务。这是一个初始化,而不是赋值。
    • 请注意,这是示例初始化而不是赋值。
    【解决方案4】:

    extern 关键字的目的是赋予实体外部链接。无论是在 a 或定义中的声明中使用它都没有区别。您发布的代码绝对没有错误。

    如果您更愿意从“导出与导入”的角度来考虑它,那么将extern 关键字应用于非定义声明意味着我们正在导入 在其他翻译单元中定义的实体。当extern 关键字应用于定义时,意味着我们正在导出这个实体以供其他翻译单元使用。 (虽然值得注意的是,“导出与导入”并不完全是思考 C 链接概念的标准方式。)

    您不会经常看到定义中使用的关键字的原因是因为在 C 文件范围定义中默认具有外部链接。所以写

    extern int i = 10;
    

    是有效的,但是是多余的,因为它等价于plain

    int i = 10;
    

    然而,在实际代码中,您可能会不时看到人们在函数声明和定义中使用这个关键字,即使它在那里也是多余的

    extern void foo(int i); /* `extern` is superfluous */
    ...
    extern void foo(int i) /* `extern` is superfluous */
    {
      /* whatever */
    }
    

    【讨论】:

    • 我错误地对此投了反对票,并且来不及纠正它。为了弥补平衡,我找到了你给我的另一个答案,我喜欢并提高了它。并不是说你需要它;)抱歉。
    猜你喜欢
    • 2011-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多