【问题标题】:Is extern keyword in C redundant?C中的extern关键字是多余的吗?
【发布时间】:2016-10-06 01:19:32
【问题描述】:

我发现我可以在不使用 extern 的情况下达到预期的结果(尽管我同意它给读者一些关于变量的提示)。在某些情况下,使用 extern 会产生不希望的结果。

xyz.h

int i;

file1.c

#include "xyz.h"
....
i=10;
....

file2.c

#include "xyz.h"
main()
{
printf("i=%d\n",i);
}

当然,这是一个大项目,为了简单理解,把它分解了。使用 extern 关键字,我无法得到想要的结果。事实上,我使用“extern”方法得到了变量 i 的链接器错误。

采用“外部”方法的代码,

file1.c

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

file2.c

extern int i;
foo()
{
printf("i=%d\n",i);
}

这导致链接器错误。我只是想知道为什么它在第一种情况下有效,以及在不使用关键字“extern”的情况下我们无法做到这一点的实际情况。谢谢。

【问题讨论】:

  • 在第二种情况下,你有两个函数叫做main
  • 道歉。 file2.c 没有 main()
  • 嗯...链接的“重复”如何真的是重复的。 OP 的问题是关于避免 extern,而所谓的重复是关于正确使用extern
  • @Harsha:我不明白您的第二种方法怎么可能出现链接器错误。修复函数名称后,第二种方法不再有任何链接器问题。从中获取链接器错误的唯一方法是忘记将目标文件之一提供给链接器。

标签: c keyword extern


【解决方案1】:

形式上,您的第一个程序是无效的。在头文件中定义一个变量,然后将该头文件包含到多个翻译单元中,最终会导致同一实体的多个定义与外部链接。这是 C 中的约束违规。

6.9 外部定义

5 外部定义是一个外部声明,它也是一个 函数的定义(内联定义除外)或 目的。如果使用外部链接声明的标识符用于 表达式(不是作为 sizeof 运算符的操作数的一部分 其结果是一个整数常量),在整个程序的某处 标识符应该只有一个外部定义; 否则,不得超过一个。

在您的第一个示例中,i 的定义是一个暂定定义(正如 cmets 中提到的那样),但它变成了一个常规的完整的外部定义 em> of i 在每个包含头文件的翻译单元的末尾。因此,从“整个程序”的角度来看,该定义的“暂时性”并没有改变任何东西。它与手头的事情并没有真正的密切关系(除了下面的一点评论)。

使您的第一个示例能够顺利编译的原因是一个流行的编译器扩展,甚至在语言标准中也提到了这一点。

J.5 常用扩展

J.5.11 多个外部定义

1可能不止一个 对象标识符的外部定义,有或没有 显式使用关键字 extern;如果定义不一致, 或多个初始化,行为未定义(6.9.2)。

(很可能最初导致 C 中编译器扩展的原因是暂定定义支持的一些实现特性,但在抽象语言级别,暂定定义与此无关。)

您的第二个程序对i 有效(顺便说一句,C 不再支持隐式int)。我看不出你怎么能从中得到任何链接器错误。

【讨论】:

  • int i; 文件范围只是一个暂定定义,而不是实际定义。
  • @Olaf:尽管如此,暂定的定义在两个 TU 中都变为现实,不是吗?
  • @Olaf:是的,但这完全不是重点。暂定定义成为翻译单元末尾的常规定义。在两个不同的翻译单元中定义相同的外部对象在 C 中和在 C++ 中一样非法。
  • @KerrekSB:我只是想从标准中找出答案。我实际上不确定他们没有引用 same 对象。 IIRC,extern 确实是不必要的。避免对错字进行实际定义是一种很好的做法。 (从来没有考虑过标准到底是什么,因为上述原因我只是extern)。
  • @AnT:我已经更改了 file2.c。它没有 main()。对此表示歉意
【解决方案2】:

至少有两种情况extern 是有意义的而不是“冗余的”:

  1. 对于文件范围内的对象(不是函数),它声明具有外部链接的对象而不提供暂定定义;暂定定义在翻译单元的末尾变成完整的定义,并且不允许在多个翻译单元中使用外部链接定义相同的标识符。

  2. 在块范围内(在函数中),extern 允许您声明和访问具有外部链接的对象或函数,而无需将标识符带入文件范围(因此它在声明的块之外不可见)。如果名称可能与文件范围内的其他内容冲突,这很有用。例如:

    static int a;
    int foo(void)
    {
        return a;
    }
    int bar(void)
    {
        extern int a;
        return a;
    }
    

    如果没有bar 中的extern 关键字,int a; 将产生一个局部变量(自动存储)。

【讨论】:

  • 那么,foo 访问静态全局 'a' 而 bar 指的是在其他文件中定义的 'a'?
  • @Harsha:没错。
  • 非常巧妙。如果我是对的,就像 C++ 中的命名空间一样
  • :我可以知道为什么第一种情况下的代码对我有用吗?我的意思是,它可能在哪些情况下起作用
  • @Harsha:尽管它不是有效的 C,但 Unix 链接器创建了试探定义的传统 remaining tentative,作为“通用”符号,而不是成为常规定义。我认为这就是您的程序有效的原因。如果您使用的是 GCC 或 clang,请尝试将 -fno-common 添加到编译器命令行并查看它是否中断。
猜你喜欢
  • 2020-04-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-16
  • 2010-10-25
相关资源
最近更新 更多