【问题标题】:static declaration of m follows non-static declarationm 的静态声明遵循非静态声明
【发布时间】:2013-06-11 10:08:23
【问题描述】:

我正在尝试一个小例子来了解静态外部变量及其用途。静态变量是局部作用域,外部变量是全局作用域。

static5.c

#include<stdio.h>
#include "static5.h"
static int m = 25;
int main(){
 func(10);
 return 0;
}

static5.h

#include<stdio.h>
int func(val){
 extern int m;
 m = m + val;
 printf("\n value is : %d \n",m);
}

gcc static5.c static5.h

o/p:

static5.c:3: error: static declaration of m follows non-static declaration
static5.h:3: note: previous declaration of m was here

已编辑

正确的程序:

a.c:
#include<stdio.h>
#include "a1_1.h"
int main(){
 func(20);
 return 0;
}

a1.h:
static int i = 20;

a1_1.h:
#include "a1.h"
int func(val){
 extern int i;
 i = i + val;
 printf("\n i : %d \n",i);
}

这很好用。但这被编译成单个编译单元。因此可以访问静态变量。在整个编译单元中,我们不能通过使用 extern 变量来使用静态变量。

【问题讨论】:

  • extern int m; 不应该在函数体之外吗?
  • m 在 static5.c 中被声明为静态,因此范围在文件内。所以错误。
  • 那么问题是什么? m 对 static5.c 文件是静态的,因此即使使用 extern 声明它也无法在其他任何地方访问
  • 在头文件中包含任何函数定义是不寻常的并且具有潜在危险。

标签: c


【解决方案1】:

static 有一个非常简单的逻辑。如果一个变量是static,这意味着它是一个全局变量,但它的作用域仅限于它被定义的地方(即只在那里可见)。例如:

  • 函数外:全局变量但仅在文件内可见(实际上是编译单元)
  • 函数内部:全局变量,但仅在函数内部可见
  • (C++) 类内部:全局变量,但仅对类可见

现在让我们看看 C11 标准对 staticextern 的规定(重点是我的):

6.2.2.3

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

6.2.2.4

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

6.2.2.7

如果在翻译单元中,相同的标识符同时出现在内部和外部链接中,则行为未定义。

所以标准首先说,如果你有:

static int m;
extern int m;

那么第二个声明(带有extern)将考虑第一个声明,最后m仍然是static

但是,在任何其他情况下,如果存在具有内部和外部链接的声明,则行为未定义。这实际上给我们留下了一个选择:

extern int m;
static int m;

即,extern 声明在 static 声明之前。在这种未定义行为的情况下,gcc 足以给你错误。

【讨论】:

  • 您好,感谢您的精彩解释。但是让我感到困惑的是,有人会同时使用 staticextern - 它似乎在告诉我“我希望 var a 仅与此文件关联(静态),我会将它(外部)公开给所有其他文件” .听起来很矛盾。我的理解正确吗?如果没有,什么时候需要同时使用staticextern?提前致谢。
  • @unheilig,在您的个人项目中确实如此,但总的来说并非没有优点。在static 之前extern 的情况下,想象一个带有带有外部函数声明的通常头文件的库。您可以通过在包含标题之前将它们声明为static 来覆盖其中的一些函数。例如,使用这种方法可以实现一种启用编译时的valgrind
  • 另一种方式可能有问题,因为在extern声明之后,编译器会生成链接到该声明的代码,但后来你说它应该是static,所以编译器必须返回并修复以前的代码。由于 C 编译器是单通的,这充其量是烦人的。
【解决方案2】:

记住这一点(引用 Eli Bendersky):

  • 函数内的静态变量将其值保持在 调用。
  • 静态全局变量或函数仅在 声明的文件

在您的代码中,static int m = 25; 表示m范围仅限于该文件,也就是说,它只在static5.c 内部可见,其他任何地方都看不到。

如果您想在static5.c 之外使用m,请确保从变量声明中删除关键字static

有关更规范的解释以及示例,请参阅this answer by Eli Bendersky

编辑:(根据 Klas 的建议) **实际范围是编译单元,而不是源文件。 编译单元是文件在预处理步骤之后的样子

【讨论】:

  • 范围是编译单元,不是源文件。编译单元是文件在预处理步骤之后查找的方式,然后 func 在同一个文件中。
  • @KlasLindbäck 感谢您的评论!我会更新我的答案。
  • @NlightNFortis:它只有一个编译单元。我做 gcc static5.c 和 static5.h。代码被编译成单个编译单元。但我仍然得到了错误。
  • @Angus 是的,preprocessor 使用该文件的源代码更改了 static5.c 中的 include "static5.h",因此错误消息表明,当需要实际编译时,编译器会先找到一个 m 的 non-static 声明(static5.h 内的 func 内的那个),然后再找到静态的声明(在 static5.c 文件内)。
  • 函数内部的静态变量实际存储在哪里?是否也像常规全局变量一样存储在.data 中?
【解决方案3】:

问题与错误消息中的说明完全相同。 m 被声明为普通的int,但后来被定义为static int

extern 告诉编译器/链接器在全局变量表中查找变量。

static(在函数之外)告诉编译器从全局变量表中排除该变量。

你看到冲突了吗?

要解决此问题,请从定义中删除 static 关键字,或将定义移到包含 static5.h 的上方。

应该注意,您设计文件的方式并不是最佳实践。包含文件通常不包含函数。

【讨论】:

  • 一开始我也是这么想的,但标准在这方面更加灵活。如果static 声明出现在extern 声明之前,那完全没问题。请参阅我的答案以获取参考。唯一的问题是 extern 声明是否在前。
  • @Shahbaz 感谢您的澄清。我不确定 extern 声明是否是错误的或者是多余的。
【解决方案4】:

在声明 m 时删除关键字 static,错误将被删除,您将能够得到答案为 50。 static 关键字使范围限制在文件内。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-10
    • 1970-01-01
    • 2018-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多