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

时间:2016-10-17 11:34:25

标签: c external language-lawyer declaration linkage

我一直在调查何时可以混合使用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和没有存储说明符声明的全局声明混合起来有什么作用?

2 个答案:

答案 0 :(得分:1)

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

  

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

问题是声明标识符与定义之间的区别。第一个说“将会有一个这种类型的标识符X”。第二个说“这是我要打电话给X这种类型的东西”。

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

  • 使用变量会更难。只需声明变量定义它 - 因此您可以在定义时初始化它。如果您只想 声明,则需要使用关键字extern - 但是您也无法初始化它。你说“会有一个名为X的变量” - 所以你也不能冒然决定它的定义!

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

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

答案 1 :(得分:0)

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

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

变量(强调我的):

  

如果声明对象的文件范围标识符或   function包含存储类说明符static,标识符   有内部联系。

     

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

功能:

  

如果函数标识符的声明没有   存储类说明符,其链接确定就像它一样   用存储类说明符extern声明。如果   对象标识符的声明具有文件范围而没有   存储类说明符,其链接是外部的。