C 中的 extern 关键字是多余的吗?

Is extern keyword in C redundant?

我发现我可以在不使用 extern 的情况下获得预期的结果(尽管我同意它给了 reader 某种关于变量的提示)。在某些情况下,使用 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 的链接器错误。

代码采用 "extern" 方法,

file1.c

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

file2.c

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

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

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

6.9 External definitions

5 An external definition is an external declaration that is also a definition of a function (other than an inline definition) or an object. If an identifier declared with external linkage is used in an expression (other than as part of the operand of a sizeof operator whose result is an integer constant), somewhere in the entire program there shall be exactly one external definition for the identifier; otherwise, there shall be no more than one.

第一个示例中 i 的定义是 暂定定义 (正如评论中提到的那样),但它变成了一个常规的完整定义外部定义 of i 在包含头文件的每个翻译单元的末尾。因此,从 "whole program" 的角度来看,该定义的 "tentativeness" 没有任何改变。这与手头的事情并没有真正的关系(除了下面的一点评论)。

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

J.5 Common extensions

J.5.11 Multiple external definitions

1 There may be more than one external definition for the identifier of an object, with or without the explicit use of the keyword extern; if the definitions disagree, or more than one is initialized, the behavior is undefined (6.9.2).

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

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

至少有 2 种情况 extern 有意义而不是 "redundant":

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

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

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

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