变量是否可以同时声明为静态的和外部的?

时间:2021-06-20 13:37:54

Why the following doesn't compile?

为什么以下内容不编译?

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

but if you reverse the order, it compiles fine.

但是如果你改变顺序,它会编译得很好。

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

What is going on here?

这是怎么回事?

4 个解决方案

#1


16  

This is specifically given as an example in the C++ standard when it's discussing the intricacies of declaring external or internal linkage. It's in section 7.1.1.7, which has this exert:

当讨论复杂的声明外部或内部链接时,这是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

Section 3.5.6 discusses how extern should behave in this case.

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

What's happening is this: static int i (in this case) is a definition, where the static indicates that i has internal linkage. When extern occurs after the static the compiler sees that the symbol already exists and accepts that it already has internal linkage and carries on. Which is why your second example compiles.

所发生的是:静态int i(在本例中)是一个定义,其中静态表示i具有内部链接。当发生在静态之后的extern时,编译器将看到符号已经存在,并接受它已经具有内部链接并继续进行。这就是第二个示例编译的原因。

The extern on the other hand is a declaration, it implicitly states that the symbol has external linkage but doesn't actually create anything. Since there's no i in your first example the compiler registers i as having external linkage but when it gets to your static it finds the incompatible statement that it has internal linkage and gives an error.

另一方面,extern是一个声明,它隐式地声明了符号具有外部链接,但实际上并没有创建任何东西。因为在你的第一个例子中没有i编译器将i注册为有外部链接但是当它到达你的静态时它会找到不相容的语句它有内部链接并给出一个错误。

In other words it's because declarations are 'softer' than definitions. For example, you could declare the same thing multiple times without error, but you can only define it once.

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

Whether this is the same in C, I do not know (but netcoder's answer below informs us that the C standard contains the same requirement).

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

#2


10  

For C, quoting the standard, in C11 6.2.2: Linkage of identifiers:

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

3) If the declaration of a file scope identifier for an object or a function contains the storage-class specifier static, the identifier has internal linkage.

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

4) For an identifier declared with the storage-class specifier extern in a scope in which a prior declaration of that identifier is visible, if the prior declaration specifies internal or external linkage, the linkage of the identifier at the later declaration is the same as the linkage specified at the prior declaration. If no prior declaration is visible, or if the prior declaration specifies no linkage, then the identifier has external linkage.

4)存储类的一个标识符声明说明符走读生的范围之前声明的标识符是可见的,如果之前声明指定内部或外部链接,链接后声明的标识符是一样的在指定的链接之前声明。如果不可见先验声明,或者先验声明没有指定链接,则标识符具有外部链接。

(emphasis-mine)

(我特别强调)

That explains the second example (i will have internal linkage). As for the first one, I'm pretty sure it's undefined behavior:

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

7) If, within a translation unit, the same identifier appears with both internal and external linkage, the behavior is undefined.

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

...because extern appears before the identifier is declared with internal linkage, 6.2.2/4 does not apply. As such, i has both internal and external linkage, so it's UB.

…因为在使用内部链接声明标识符之前会出现extern,因此不应用6.2.2/4。因此,我有内外连杆,所以它是UB。

If the compiler issues a diagnostic, well lucky you I guess. It could compile both without errors and still be compliant to the standard.

如果编译器发出一个诊断,我猜你很幸运。它可以在没有错误的情况下进行编译,并且仍然符合标准。

#3


1  

In Microsoft Visual Studio, both versions compile just fine. On Gnu C++ you get an error.

在Microsoft Visual Studio中,两个版本的编译都很好。在Gnu c++中,您会得到一个错误。

I'm not sure which compiler is "correct". Either way, having both lines doesn't make much sense.

我不确定哪个编译器是“正确的”。不管怎样,两条线都有意义。

extern int i means that the integer i is defined in some other module (object file or library). This is a declaration. The compiler will not allocate storage the i in this object, but it will recognize the variable when you are using it somewhere else in the program.

extern int i表示在其他模块(对象文件或库)中定义的整型i。这是一个宣言。编译器不会在这个对象中分配i的存储,但是当您在程序的其他地方使用它时,它会识别这个变量。

int i tells the compiler to allocate storage for i. This is a definition. If other C++ (or C) files have int i, the linker will complain, that int i is defined twice.

我告诉编译器为i分配存储,这是一个定义。如果其他c++(或C)文件有int i,链接器会抱怨说,int i被定义了两次。

static int i is similar to the above, with the extra functionality that i is local. It cannot be accessed from other module, even if they declare extern int i. People are using the keyword static (in this context) to keep i localize.

静态int i与上面类似,具有额外的功能i是本地的。它不能从其他模块访问,即使它们声明了extern int i。

Hence having i both declared as being defined somewhere else, AND defined as static within the module seems like an error. Visual Studio is silent about it, and g++ is silent only in a specific order, but either way you just shouldn't have both lines in the same source code.

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

#4


1  

C++:

c++:

7.1.1 Storage class specifiers [dcl.stc]

7) A name declared in a namespace scope without a storage-class-specifier has external linkage unless it has internal linkage because of a previous declaration and provided it is not declared const. Objects declared const and not explicitly declared extern have internal linkage.

7)没有存储类说明符的名称空间范围内声明的名称具有外部链接,除非它具有内部链接,因为之前的声明并没有声明为const。声明的const和未显式声明的extern对象具有内部链接。

So, the first one attempts to first gives i external linkage, and internal afterwards.

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

The second one gives it internal linkage first, and the second line doesn't attempt to give it external linkage because it was previously declared as internal.

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

8) The linkages implied by successive declarations for a given entity shall agree. That is, within a given scope, each declaration declaring the same variable name or the same overloading of a function name shall imply the same linkage. Each function in a given set of overloaded functions can have a different linkage, however.
[ Example:

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
[...]

#1


16  

This is specifically given as an example in the C++ standard when it's discussing the intricacies of declaring external or internal linkage. It's in section 7.1.1.7, which has this exert:

当讨论复杂的声明外部或内部链接时,这是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

Section 3.5.6 discusses how extern should behave in this case.

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

What's happening is this: static int i (in this case) is a definition, where the static indicates that i has internal linkage. When extern occurs after the static the compiler sees that the symbol already exists and accepts that it already has internal linkage and carries on. Which is why your second example compiles.

所发生的是:静态int i(在本例中)是一个定义,其中静态表示i具有内部链接。当发生在静态之后的extern时,编译器将看到符号已经存在,并接受它已经具有内部链接并继续进行。这就是第二个示例编译的原因。

The extern on the other hand is a declaration, it implicitly states that the symbol has external linkage but doesn't actually create anything. Since there's no i in your first example the compiler registers i as having external linkage but when it gets to your static it finds the incompatible statement that it has internal linkage and gives an error.

另一方面,extern是一个声明,它隐式地声明了符号具有外部链接,但实际上并没有创建任何东西。因为在你的第一个例子中没有i编译器将i注册为有外部链接但是当它到达你的静态时它会找到不相容的语句它有内部链接并给出一个错误。

In other words it's because declarations are 'softer' than definitions. For example, you could declare the same thing multiple times without error, but you can only define it once.

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

Whether this is the same in C, I do not know (but netcoder's answer below informs us that the C standard contains the same requirement).

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

#2


10  

For C, quoting the standard, in C11 6.2.2: Linkage of identifiers:

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

3) If the declaration of a file scope identifier for an object or a function contains the storage-class specifier static, the identifier has internal linkage.

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

4) For an identifier declared with the storage-class specifier extern in a scope in which a prior declaration of that identifier is visible, if the prior declaration specifies internal or external linkage, the linkage of the identifier at the later declaration is the same as the linkage specified at the prior declaration. If no prior declaration is visible, or if the prior declaration specifies no linkage, then the identifier has external linkage.

4)存储类的一个标识符声明说明符走读生的范围之前声明的标识符是可见的,如果之前声明指定内部或外部链接,链接后声明的标识符是一样的在指定的链接之前声明。如果不可见先验声明,或者先验声明没有指定链接,则标识符具有外部链接。

(emphasis-mine)

(我特别强调)

That explains the second example (i will have internal linkage). As for the first one, I'm pretty sure it's undefined behavior:

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

7) If, within a translation unit, the same identifier appears with both internal and external linkage, the behavior is undefined.

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

...because extern appears before the identifier is declared with internal linkage, 6.2.2/4 does not apply. As such, i has both internal and external linkage, so it's UB.

…因为在使用内部链接声明标识符之前会出现extern,因此不应用6.2.2/4。因此,我有内外连杆,所以它是UB。

If the compiler issues a diagnostic, well lucky you I guess. It could compile both without errors and still be compliant to the standard.

如果编译器发出一个诊断,我猜你很幸运。它可以在没有错误的情况下进行编译,并且仍然符合标准。

#3


1  

In Microsoft Visual Studio, both versions compile just fine. On Gnu C++ you get an error.

在Microsoft Visual Studio中,两个版本的编译都很好。在Gnu c++中,您会得到一个错误。

I'm not sure which compiler is "correct". Either way, having both lines doesn't make much sense.

我不确定哪个编译器是“正确的”。不管怎样,两条线都有意义。

extern int i means that the integer i is defined in some other module (object file or library). This is a declaration. The compiler will not allocate storage the i in this object, but it will recognize the variable when you are using it somewhere else in the program.

extern int i表示在其他模块(对象文件或库)中定义的整型i。这是一个宣言。编译器不会在这个对象中分配i的存储,但是当您在程序的其他地方使用它时,它会识别这个变量。

int i tells the compiler to allocate storage for i. This is a definition. If other C++ (or C) files have int i, the linker will complain, that int i is defined twice.

我告诉编译器为i分配存储,这是一个定义。如果其他c++(或C)文件有int i,链接器会抱怨说,int i被定义了两次。

static int i is similar to the above, with the extra functionality that i is local. It cannot be accessed from other module, even if they declare extern int i. People are using the keyword static (in this context) to keep i localize.

静态int i与上面类似,具有额外的功能i是本地的。它不能从其他模块访问,即使它们声明了extern int i。

Hence having i both declared as being defined somewhere else, AND defined as static within the module seems like an error. Visual Studio is silent about it, and g++ is silent only in a specific order, but either way you just shouldn't have both lines in the same source code.

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

#4


1  

C++:

c++:

7.1.1 Storage class specifiers [dcl.stc]

7) A name declared in a namespace scope without a storage-class-specifier has external linkage unless it has internal linkage because of a previous declaration and provided it is not declared const. Objects declared const and not explicitly declared extern have internal linkage.

7)没有存储类说明符的名称空间范围内声明的名称具有外部链接,除非它具有内部链接,因为之前的声明并没有声明为const。声明的const和未显式声明的extern对象具有内部链接。

So, the first one attempts to first gives i external linkage, and internal afterwards.

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

The second one gives it internal linkage first, and the second line doesn't attempt to give it external linkage because it was previously declared as internal.

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

8) The linkages implied by successive declarations for a given entity shall agree. That is, within a given scope, each declaration declaring the same variable name or the same overloading of a function name shall imply the same linkage. Each function in a given set of overloaded functions can have a different linkage, however.
[ Example:

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
[...]