变量可以声明为static还是extern?

为什么以下不编译?

... extern int i; static int i; ... 

但如果你颠倒了订单,那就很好。

 ... static int i; extern int i; ... 

这里发生了什么?

当它讨论声明外部或内部链接的复杂性时,这是C ++标准中的一个例子。 这是在第7.1.1.7节中,它具有以下function:

 static int b ; // b has internal linkage extern int b ; // b still has internal linkage extern int d ; // d has external linkage static int d ; // error: inconsistent linkage 

第3.5.6节讨论了extern在这种情况下应该如何表现。

发生的事情是这样的: static int i (在这种情况下)是一个定义,其中static表示i有内部链接。 当static发生extern后编译器看到符号已经存在并接受它已经有内部链接进行。 这就是你的第二个例子编译的原因。

另一方面, extern是一个声明,它隐含地声明该符号具有外部链接但实际上并没有创建任何东西。 由于第一个例子中没有i ,编译器将i注册为具有外部链接,但是当它到达static时,它会发现不兼容的语句,它具有内部链接并给出错误。

换句话说,这是因为声明比定义“更柔和”。 例如,您可以多次声明相同的事情而不会出错,但您只能定义一次。

在C中是否相同,我不知道(但netcoder的答案在下面告诉我们C标准包含相同的要求)。

对于C,引用标准,在C11 6.2.2中: 标识符的链接

3)如果对象或函数的文件范围标识符的声明包含static的存储类说明符,则标识符具有内部链接。

4) 对于在该标识符的先前声明可见的范围内使用存储类说明符extern声明的标识符 ,如果先前声明指定内部或外部链接, 则后面声明中的标识符的链接与在先前声明中指定的联系 。 如果没有先前声明可见,或者先前声明未指定链接,则标识符具有外部链接。

(重点矿)

这解释了第二个例子( i将有内部联系)。 至于第一个,我很确定它是未定义的行为:

7)如果在翻译单元内,同一标识符同时出现内部和外部链接,则行为未定义。

…因为extern出现使用内部链接声明标识符之前 ,6.2.2 / 4不适用。 因此, i有内部和外部联系,所以它是UB。

如果编译器发出诊断信息,我猜你很幸运。 它可以无误地编译,并且仍然符合标准。

在Microsoft Visual Studio中,两个版本都可以正常编译。 在Gnu C ++上你会收到一个错误。

我不确定哪个编译器是“正确的”。 无论哪种方式,两条线都没有多大意义。

extern int i表示整数i在某个其他模块(目标文件或库)中定义。 这是一个声明。 编译器不会在此对象中为i分配存储,但是当您在程序中的其他位置使用它时,它将识别该变量。

int i告诉编译器为i分配存储空间。 这是一个定义。 如果其他C ++(或C)文件具有int i ,则链接器将抱怨,int i被定义两次。

static int i与上面类似,具有i本地的额外function。 它无法从其他模块访问,即使它们声明了extern int i 。 人们使用关键字static(在此上下文中)来保持本地化。

因此, i已经声明在其他地方被定义,并且在模块中定义为静态似乎是一个错误。 Visual Studio对此保持沉默,g ++只在特定的顺序中保持沉默,但无论哪种方式,您都不应该在同一源代码中同时拥有这两行。

C ++:

7.1.1存储类说明符[dcl.stc]

7) 在没有存储类说明符的命名空间范围内声明的名称具有外部链接,除非由于先前的声明具有内部链接,并且未将其声明为const。 声明为const且未显式声明为extern的对象具有内部链接。

因此,第一个尝试首先给出外部链接,然后是内部链接。

第二行首先给它内部链接,第二行不尝试给它外部链接,因为它先前被声明为内部。

8)对特定实体的连续声明所暗示的联系应予以同意。 也就是说,在给定范围内,声明相同变量名称或函数名称相同重载的每个声明都应表示相同的链接。 但是,给定的一组重载函数中的每个函数都可以具有不同的链接。
[例如:

 [...] static int b; // b has internal linkage extern int b; // b still has internal linkage [...] extern int d; // d has external linkage static int d; // error: inconsistent linkage [...]