本地和寄存器变量可以声明为extern吗?

我一直想知道extern是否可以在本地声明和寄存器变量。 如果它可能会受到什么限制?

可以将局部变量声明为extern吗?

是的 ,在某些情况下。

我们来看看C99 N1256标准草案 。

该标准将“局部变量”称为具有“块范围”。

6.7.1 / 5存储类说明符说:

具有块作用域的函数的标识符声明不应具有除extern之外的显式存储类说明符。

然后,对于将extern添加到局部变量意味着什么, 6.2.2 / 4标识符的链接说:

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

让我们分解这些案例。

没有事先声明

 void f() { extern int i; } 

是相同的:

 extern int i; void f() {} 

除了声明只在f内可见。

这是因为i没有事先声明可见。 所以i有外部链接(与全局变量相同的链接)。

事先声明指定没有链接

 void f() { int i; extern int i; } 

是相同的:

 void f() { extern int i; } 

因为先前的声明int i指定没有链接,因为第6段说:

以下标识符没有链接:声明为对象或函数以外的任何标识符; 声明为函数参数的标识符; 在没有存储类说明符extern的情况下声明的对象的块作用域标识符。

事先声明指定内部或外部联系

 extern int i; void f() { extern int i; } 

是相同的:

 extern int i; void f() {} 

和:

 static int i; void f() { extern int i; } 

是相同的:

 static int i; void f() {} 

因为在这两种情况下,我们分别有一个先前可见的外部和内部( static )链接声明。

初始化本地外部

无效的C:

 void f() { extern int i = 0; } 

因为块范围声明具有初始化。

有效C:

 extern int i = 0; void f() {} 

但可以说是糟糕的风格,因为相当于较短的:

 int i = 0; void f() {} 

因为6.7.8初始化说:

如果标识符的声明具有块范围,并且标识符具有外部或内部链接,则声明不应具有标识符的初始化器。

  1. 可以将局部变量声明为extern吗?

不可以。但是全局变量可以在本地声明。

 // file1.c int Count; // file2.c void foo(void) { extern int Count; Count++; } 
  1. 注册变量可以声明为extern吗?

不可以。变量可能不是externregister

C11博士6.7.1存储级说明符
1个存储类说明符:
typedef
extern
static
_Thread_local
auto
register
约束
2最多可以在声明中的声明说明符中给出一个存储类说明符,但_Thread_local可能出现在staticextern

6.9 C99的外部定义指出:

存储类说明符auto和register不应出现在外部声明中的声明说明符中。

您只能将全局变量定义为extern 。 告诉编译器(和链接器)它在别处定义。

局部变量仅存在于本地作用域中,因为它是在堆栈或寄存器中创建的。 当执行不在范围内(不再)时,堆栈被展开(因此可用空间变得可用)或者寄存器用于其他事情,并且变量不再存在(不再)。

因此,定义一个本地extern将是“奇怪的”和不可能的(由于堆栈的使用)。

register variable这个短语对我来说并不清楚,所以我会粗略猜测OP真正好奇的是什么,并将原来的问题Could local variables be declared with extern specifier?Could local variables be declared with extern specifier? ,由以下片段说明:

 int main() { extern int x; // Is this OK? return 0; } 

答案是肯定的。

范围(可见性)和存储是两个独立且相互关联的概念。 这里, x是一个局部变量(范围),它只在该块中可见。 extern指示存储,这意味着这只是一个声明,这个变量在其他地方定义。 建议C标准明确参考。

对于省略的register部分,我假设OP表示一个带register存储类指定符的变量,如register int x 。 那么,同时指定registerextern是非法的。

 int main() { extern auto int x; // This is wrong. return 0; } 

At most, one storage-class specifier may be given in the declaration specifiers in a declaration, except that _Thread_local may appear with static or extern.

对称问题是:指定autoregister全局变量或外部变量是否有效,这正是Alexey Frunze的回答。

 auto int x; // This is wrong. int main() { return 0; }