在全局范围内将声明与extern,static和no存储说明符混合使用
我一直在研究何时可以在全局范围内混合使用extern
, static
和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();
我用gcc
和clang
获得了相同的结果,但是我找不到一个有效的模式和无效的模式。
这里有什么逻辑吗?
关于将所声明的全局声明与extern
, static
和no存储说明符混合,C标准有何说法?
如果您定义不带关键字static
的标识符,它将在对象文件中发布,并可由其他模块访问。 因此,如果您再次在另一个模块中定义没有static
标识符,则会产生冲突:两个已发布的标识符。
如果您使用关键字
static
,则(大部分)其余部分不适用。
问题是声明标识符和定义标识符之间的区别。 第一个说“将有一个这种类型的标识符X
”。 第二个说“这就是我将要称之为X
的东西”。
-
使用function很容易:不提供身体,它只是一个声明 。 提供身体,这也是一个定义 。 您可以使用
extern
在头文件中明确显示它,但由于它是默认值,因此不常见。 -
随着变量,它更难。 简单地声明变量也可以定义它 – 因此您可以在定义变量时对其进行初始化。 如果你只想声明它,你需要使用关键字
extern
– 但是你也不能初始化它。 你说的是“将会有一个名为X
的变量” – 所以你不能冒昧地决定它的定义!
这就是为什么在头文件中所有变量都应该显式声明为extern
或static
:
- 第一种是通常的:每个人都可以访问一个公共变量。 不要忘记,在某个模块中,您需要在没有
extern
关键字的情况下提供具有可选初始化值的实际定义 。 - 第二种情况很少见:包含头文件的每个模块都有自己的,具有该特定名称的非冲突变量。 编译器可能无法为它分配内存(特别是如果它是一个常量) – 但如果它确实分配了内存,那么它在每个模块中都会有所不同。 你为什么要这样做? 也许该头文件的(强制)内联函数需要每个模块都有自己的副本…
首先,在标准C中没有任何称为“全局”的东西,它是一个经常被误用的术语,可能意味着几个不同的东西。
如果在文件范围 (您称之为“全局”)声明某些内容并且未指定存储类,则默认为外部链接。 您无法在文件范围内声明没有链接的内容。 这由C11 6.2.2规定。
变量(强调我的):
如果对象或函数的文件范围标识符的声明包含存储类说明符static,则标识符具有内部链接。
对于在该标识符的先前声明可见的范围内使用存储类说明符extern声明的标识符,如果先前声明指定内部或外部链接,则后面声明中的标识符的链接与链接相同在先前声明中指明。 如果没有先前声明可见,或者先前声明未指定链接,则标识符具有外部链接。
function:
如果函数的标识符声明没有存储类说明符,则确定其链接与使用存储类说明符extern声明的完全相同。 如果对象的标识符声明具有文件范围而没有存储类说明符,则其链接是外部的。