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

Mixing declarations with extern, static and no storage specifier in global scope

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

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

/* 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 上得到了完全相同的结果,但我无法找到有效和无效的模式。

这里有逻辑吗?

C 标准对于混合使用 externstatic 和无存储说明符声明的全局声明有何规定?

首先,在标准 C 中没有所谓的 "global",它是一个经常被误用的术语,它可以表示多种不同的意思。

如果你在文件范围(你所谓的"global")声明了一些东西并且没有指定存储class,那么它默认为外部连锁。您不能在文件范围内声明没有链接的内容。这是由 C11 6.2.2.

指定的

变量(强调我的):

If the declaration of a file scope identifier for an object or a function contains the storage-class specifier static, the identifier has internal linkage.

For an identifier declared with the storage-class specifier extern in a scope in which a prior declaration of that identifier is visible, if the prior declaration specifies internal or external linkage, the linkage of the identifier at the later declaration is the same as the linkage specified at the prior declaration. If no prior declaration is visible, or if the prior declaration specifies no linkage, then the identifier has external linkage.

函数:

If the declaration of an identifier for a function has no storage-class specifier, its linkage is determined exactly as if it were declared with the storage-class specifier extern. If the declaration of an identifier for an object has file scope and no storage-class specifier, its linkage is external.

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

If you use the keyword static, then (most of) the rest of this doesn't apply.

问题在于声明标识符和定义标识符之间的区别。第一个说 "there's going to be an identifier X with this type"。第二个说 "here's something that I'm going to call X of this type".

  • 使用函数很简单:不提供正文,它只是一个声明。提供主体,它也是一个 定义。您可以使用 extern 在头文件中明确表示,但由于它是默认设置,所以这并不常见。

  • 有了变量就更难了。简单地 声明 变量 定义 它 - 因此你可以在 定义 它的同时初始化它。如果你只想 声明它,你需要使用关键字extern - 但是你也不能初始化它。你说的是 "there's going to be a variable called X" - 所以你也不能草率地决定它的定义!

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

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