了解外部存储类
这个问题在这里已经有了答案:
变量a
被声明并定义为行中的全局变量 :
int a = 20;
extern
行只是告诉main()
函数的作用域, a
是在另一个地方定义的。
在这种情况下,使用extern
并不是真的必要。 您可以在main()
函数之前声明并定义a
,然后main()
会熟悉它。
通常,当你想使用在另一个源文件中定义的变量或函数时(而不仅仅是在相同的源文件中),你可以使用extern
。
extern
在语法上是一个“存储类”关键字。 但是没有这样的存储类。 C有“静态存储”,“动态存储( malloc
等)和”自动存储“(局部变量,通常用堆栈表示)。
如果标识符在块范围内声明为extern
,则表示该声明引用了外部定义。 如果被声明的实体是一个对象,那么它就具有静态存储,只是因为外部对象具有静态存储。 它也可以是一个功能; 函数不被认为具有存储。
在C中,有一个叫做“连接”的概念。 在文件范围外的任何函数之外声明的对象和函数可以具有“外部”或“内部”链接。
如果我们在块范围内有extern
,就像在示例程序中一样,可以在文件范围或嵌套范围内预先声明相同的名称,如下所示:
static int x;
/* ... */
{
extern int x;
}
在这里,内部x
是指外部x
,尽管是“外部”,但由于“静态”而具有内部联系。
简而言之, extern
通常意味着“引用先前的声明,如果没有声明,则将其声明为具有外部链接的标识符”。
“外部”这个词指的是两个单独的概念:前面提到的“外部联系”,也指“任何功能之外”的意思,如在“外部声明”中。 令人困惑的是,“外部声明”,如上面的static int x
,可以有“内部联系”!
在你的程序中,事情是正确的,因为块范围extern
声明a
和后面的int a = 20
,它们在不同的范围内,恰好相互独立。
int a = 20;
是一个外部声明,它也是一个外部定义(由于初始化器)。 由于在该范围内,没有事先声明a
是可见的,它得到外部链接。
因此,其中a
定义? 它在整个翻译单元中被定义为具有外部链接的对象。 翻译单位是什么定义a
。 a
在声明出现的程序的每个地方声明; 其定义也是一个声明。 它在main
以及翻译部门源代码的最后一行中进行了声明。
“声明”是使某个名称在某个范围内已知的语法。 这是一个在翻译节目时积极的概念。 “定义”是在某翻译单位中提供了一些对象或功能。 翻译过的单位仍然提供定义,但不需要保留关于声明的信息。 (这就是为什么当我们创建库时,我们提供头文件和其中的声明!)
从你main
功能的角度来看,这个功能并不关心a
的定义。 它已宣布a
以这样的方式,如果a
被使用,那么外部定义a
与外部连接,必须存在。 该定义可能来自任何地方:可能位于同一翻译单位或另一翻译单位。
C编程语言被设计为一次通过,因此编译器只能从上到下处理每行一次。 所以考虑你的程序:
#include <stdio.h>
int main(){
extern int a;
printf("%dn",a);
return 0;
}
int a = 20;
标识符a
被声明两次,并定义一次。
在第四行之前extern int a;
,编译器不知道关于标识符a
任何信息。 声明extern int a;
在函数main
具有块范围,并将标识符a
声明为int
,并且其存储持续时间是静态的,并且链接是外部的。 因此,编译器可以编写代码,通过名称a
访问全局标识符,作为可以在另一个模块(外部链接)中定义的int
变量。 这是编译器在printf
使用的第5
行。
最后在第9行, int a = 20;
是另一种声明和定义。 这声明和定义a
一个具有静态存储持续时间的int
,以及外部链接。
如果你把int a = 20;
在main
之前,声明extern int a;
将是无用的,因为它不会添加任何东西。 我倾向于将我的main
函数和其他依赖函数放在我的源代码中,这样只需要最少量的额外声明。