在全局范围内混合使用extern、static和无存储说明符的声明



我一直在研究何时可以在全局范围内混合使用extern, static声明的变量和没有存储说明符。结果让我很困惑。

这是我发现的(每一段都是一个单独的编译单元):

/* ok */
int x;
int x;
/* ok */
int f();
int f();
/* ok */
int x;
extern int x;
/* ok */
int f();
extern int f();
/* error: static declaration follows non-static declaration */
int x;
static int x;
/* ok (no warning) */
int f();
static int f();
/* ok */
extern int x;
int x;
/* ok */
extern int f();
int f();
/* ok */
extern int x;
extern int x;
/* ok */
extern int f();
extern int f();
/* error: static declaration follows non-static declaration */
extern int x;
static int x;
/* error: static declaration follows non-static declaration */
extern int f();
static int f();
/* error: non-static declaration follows static declaration */
static int x;
int x;
/* ok (no warning) */
static int f();
int f();
/* ok */
static int x;
extern int x;
/* ok */
static int f();
extern int f();
/* ok */
static int x;
static int x;
/* ok */
static int f();
static int f();

我得到与gccclang相同的确切结果,但我找不到一个模式在什么工作,什么不工作。

这里有什么逻辑吗?

对于用extern, static和没有存储说明符声明的全局声明,C标准有什么规定?

如果您定义了一个标识符而没有关键字static,则它将在目标文件中发布,并且可以由其他模块访问。因此,如果您在另一个模块中再次定义没有static的标识符,您将得到冲突:两个已发布的标识符。

如果您使用关键字static,那么(大部分)其余部分就不适用了。

问题在于声明标识符和定义标识符之间的区别。第一个说"将有一个具有此类型的标识符X"。第二个说"这里有一个我要命名为这种类型的X的东西"。
  • 对于函数来说很简单:不提供函数体,它仅仅是一个声明。提供主体,它也是一个定义。您可以使用extern在头文件中明确说明这一点,但由于它是默认的,所以通常不这样做。

  • 使用变量更难。只要声明变量也定义了,这样你就可以在定义的同时初始化它。如果你只想声明它,你需要使用关键字extern——但是你也不能初始化它。你说的是"将有一个名为X的变量"——所以你也不能大胆地决定它的定义!

这就是为什么在头文件中所有变量都应该显式地声明externstatic:

  • 第一个是常见的:将有一个公共变量,每个人都可以访问。不要忘记,在一个模块的某个地方,您需要提供实际的定义,没有extern关键字,具有可选的初始化值。
  • 第二种是罕见的:每个包含头文件的模块都有自己的,不冲突的变量,具有特定的名称。编译器可能能够不为分配内存(特别是如果它是一个常量)-但是如果它确实分配内存,它在每个模块中都是不同的。你为什么要这么做?也许头文件的(强制)内联函数需要每个模块都有自己的副本…

首先,在标准C语言中没有所谓的"全局",这是一个经常被误用的术语,它可以指几种不同的东西。

如果你在文件作用域声明一些东西(你称之为"全局"),并且没有指定存储类,那么它默认为外部链接。你不能在文件范围内声明没有链接的东西。这是c116.2.2指定的。

变量(强调我的):

如果声明对象或对象的文件作用域标识符函数包含存储类说明符static,即标识符

对于使用存储类说明符extern in声明的标识符该标识符的先前声明可见的作用域,如果前面的声明指定了内部或外部链接,即标识符在后面声明时的链接与在先前声明中指定的链接。如果没有预先声明可见,或者如果前面的声明没有指定链接,则

功能:

如果函数的标识符声明没有存储类指定符时,其链接的确定与它完全相同使用存储类说明符extern声明。如果对象标识符的声明具有文件作用域,没有存储类说明符,它的链接是外部的。

最新更新