【问题标题】:Mixing declarations with extern, static and no storage specifier in global scope在全局范围内混合使用外部、静态和无存储说明符的声明
【发布时间】:2016-10-17 11:34:25
【问题描述】:

我一直在研究何时可以在全局范围内混合使用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 和无存储说明符声明的全局声明有何规定?

【问题讨论】:

  • 没有像 "no links" WRT 全局变量这样的东西。标准(6.2.2 5)对此非常清楚:" 如果函数标识符的声明没有存储类说明符,则其链接被确定为就好像它是使用存储类说明符声明的一样extern。如果对象标识符的声明具有文件范围且没有存储类说明符,则其链接是外部的。"
  • 与其通过测试驱动的方法学习 C,不如找本好书读一读。如果您真的想在规范中了解它,请下载标准草案(它是免费的)并自己阅读。到目前为止,您的问题太宽泛了,IMO。
  • 没有静态链接,staticextern 首先是存储类说明符。联动规范是副产品。
  • “C 标准是怎么说的” - 你为什么不自己阅读它并询问你不理解的具体方面?我们不是辅导网站。

标签: c external language-lawyer declaration linkage


【解决方案1】:

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

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

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

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

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

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

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

【讨论】:

    【解决方案2】:

    首先,标准 C 中没有所谓的“全局”,这是一个经常被误用的术语,它可能意味着多种不同的东西。

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

    变量(强调我的):

    如果声明一个对象的文件范围标识符或 函数包含存储类说明符静态、标识符 有内部联系。

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

    功能:

    如果函数标识符的声明没有 存储类说明符,它的链接是完全确定的,就好像它 使用存储类说明符 extern 声明。如果 对象标识符的声明具有文件范围,并且没有 存储类说明符,它的链接是外部的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-03
      • 1970-01-01
      相关资源
      最近更新 更多