为什么以下内容无法编译?
...
extern int i;
static int i;
...
但是如果你颠倒了订单,那就很好了。
...
static int i;
extern int i;
...
这里发生了什么?
答案 0 :(得分:17)
在讨论声明外部或内部链接的复杂性时,这是C ++标准中的一个例子。它出现在第7.1.1.7节中,其中包括:
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
有内部联系。当extern
在编译器看到符号已经存在并且接受它已经具有内部链接并继续之后发生static
时。这就是你的第二个例子编译的原因。
extern
是一个声明,它隐含地声明该符号具有外部链接但实际上并不创建任何内容。由于第一个示例中没有i
,编译器将i
注册为具有外部链接,但是当它到达static
时,它会找到不兼容的语句,表明它具有内部链接并给出错误。
换句话说,因为声明比定义“更柔和”。例如,您可以多次声明相同的事情而不会出现错误,但您只能定义一次。
在C中是否相同,我不知道(但是netcoder的答案在下面告诉我们C标准包含相同的要求)。
答案 1 :(得分:10)
对于C,引用标准,在C11 6.2.2中:标识符的链接:
3)如果对象或函数的文件范围标识符的声明包含存储类说明符
static
,则标识符具有内部链接。4)对于在该标识符的先前声明可见的范围内使用存储类说明符
extern
声明的标识符,如果先前声明指定内部或外部链接,后面声明中标识符的链接与 在先前声明中指定的联系。如果没有先前声明可见,或者先前声明未指定链接,则标识符具有外部链接。
(重点矿)
这解释了第二个例子(i
将有内部联系)。至于第一个,我很确定它是未定义的行为:
7)如果在翻译单元中,同一标识符同时出现内部和外部链接,则行为未定义。
...因为extern
出现之前标识符是通过内部链接声明的,6.2.2 / 4不适用。因此,i
具有内部和外部链接,因此它是UB。
如果编译器发出诊断信息,我猜你很幸运。它可以无错误地编译,并且仍然符合标准。
答案 2 :(得分:1)
在Microsoft Visual Studio中,两个版本都可以正常编译。 在Gnu C ++上,您会收到错误。
我不确定哪个编译器“正确”。无论哪种方式,两条线都没有多大意义。
extern int i
表示整数i
在某个其他模块(目标文件或库)中定义。这是一个声明。编译器将不在此对象中为i
分配存储,但是当您在程序中的其他位置使用该变量时,它将识别该变量。
int i
告诉编译器为i
分配存储空间。这是一个定义。如果其他C ++(或C)文件有int i
,链接器会抱怨,int i被定义了两次。
static int i
与上述类似,具有i
为本地的额外功能。即使它们声明extern int i
,也无法从其他模块访问它。人们使用关键字static(在此上下文中)来保持本地化。
因此将i
声明为在其他地方被定义,并且在模块中定义为静态似乎是一个错误。 Visual Studio对此保持沉默,g ++只在特定的顺序中保持沉默,但无论哪种方式,您都不应该在同一源代码中同时拥有这两行。
答案 3 :(得分:1)
C ++:
7)在没有存储类说明符的命名空间范围内声明的名称具有外部链接,除非它具有 内部链接,因为前面的声明,并提供它不被声明为const。声明的对象 const和未明确声明的extern具有内部链接。
所以,第一个尝试首先给出i
外部链接,然后是内部链接。
第二行首先给它内部链接,第二行不尝试给它外部链接,因为它先前被声明为内部链接。
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
[...]