在全局范围内将声明与extern,static和no存储说明符混合使用

我一直在研究何时可以在全局范围内混合使用externstatic和no storage specifier声明的变量。 结果让我很困惑。

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

 /* 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获得了相同的结果,但是我找不到一个有效的模式和无效的模式。

这里有什么逻辑吗?

关于将所声明的全局声明与externstatic和no存储说明符混合,C标准有何说法?

如果您定义不带关键字static的标识符,它将在对象文件中发布,并可由其他模块访问。 因此,如果您再次在另一个模块中定义没有static标识符,则会产生冲突:两个已发布的标识符。

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

问题是声明标识符和定义标识符之间的区别。 第一个说“将有一个这种类型的标识符X ”。 第二个说“这就是我将要称之为X的东西”。

  • 使用function很容易:不提供身体,它只是一个声明 。 提供身体,这也是一个定义 。 您可以使用extern在头文件中明确显示它,但由于它是默认值,因此不常见。

  • 随着变量,它更难。 简单地声明变量也可以定义它 – 因此您可以在定义变量时对其进行初始化。 如果你只想声明它,你需要使用关键字extern – 但是你也不能初始化它。 你说的是“将会有一个名为X的变量” – 所以你不能冒昧地决定它的定义!

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

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

首先,在标准C中没有任何称为“全局”的东西,它是一个经常被误用的术语,可能意味着几个不同的东西。

如果在文件范围 (您称之为“全局”)声明某些内容并且未指定存储类,则默认为外部链接。 您无法在文件范围内声明没有链接的内容。 这由C11 6.2.2规定。

变量(强调我的):

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

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

function:

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