13

我一直想知道是否可以在本地声明一个外部变量和一个寄存器变量。如果可以,将施加什么限制?

4

5 回答 5

13

在某些情况下,局部变量可以声明为 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(与全局变量相同的链接)。

先前的声明指定没有链接

int i;
void f() {
    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 初始化说:

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

于 2015-06-19T07:52:32.010 回答
9
  1. 局部变量可以声明为extern吗?

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

// file1.c
int Count;

// file2.c
void foo(void) {
  extern int Count;
  Count++;
}
  1. 寄存器变量可以声明为外部吗?

不,变量可能不是externand register

C11 dr 6.7.1 存储类说明符
1存储类说明符:
typedef
extern
static
_Thread_local
auto
register
约束
2 最多可以在声明的声明说明符中给出一个存储类说明符,除非它_Thread_local可能与staticextern一起出现

于 2014-12-29T22:02:05.343 回答
3

6.9 C99 状态的外部定义:

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

于 2013-01-15T10:35:06.553 回答
0

这句话register variable对我来说不是很清楚,所以我会大胆猜测一下 OP 真正好奇的是什么,并将原来的问题改写为: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. 那么,同时指定register和是非法extern的。

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;
}
于 2014-12-29T21:20:02.880 回答
0

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

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

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

于 2013-01-15T10:32:33.540 回答