外部声明遵循相同范围内的非外部声明

Extern declaration follows non-extern declaration in same scope

#include <stdio.h>

extern int x;

int main()
{
    int x;
    x = 1;
    printf("%d", x);
}
#include <stdio.h>

int main()
{
    extern int x;
    int x;
    x = 1;
    printf("%d", x);
}

我正在学习 extern 关键字,我被告知 单词 extern 用于声明变量,编译器不会分配给这个变量。 但是当我写2个代码时,第一个代码运行正常,第二个有错误。 请帮我解释一下为什么会有这种差异。谢谢

同一范围内的冲突声明

extern int x; 出现在函数外部时,它在文件范围内声明 x。然后,当 int x; 出现在函数内部时,它声明了一个与之前的 extern int x; 无关的 x 的新实例。这是C语言允许的,所以编译器不会报错。

extern int x; 出现在函数内部时,它在块范围内声明 x。然后,当 int x; 出现在它后面时,它会尝试在同一范围内声明一个不同的 x。这是C标准不允许的,所以编译器报错。

extern 关键字在这里不是特别相关 — 错误是由于同一标识符有两个相互冲突的声明而引起的。例如:

char c;
int main(void)
{
    char d;

    int c; // Allowed, new declaration in new scope.
    int d; // Not allowed, conflicting declaration in same scope.
}

声明规则

由于C发展的历史,关于C声明的规则有一些不规范之处。在文件范围内考虑这些声明:

extern int x;
int x;

第一个声明做(或不做)几件事:

  • 它说 xint 的标识符。
  • 它说 x 具有外部链接,这意味着它可以(在链接对象模块期间)引用在其他地方声明的名为 x 的对象。
  • 它没有定义 int

第二次声明:

  • 它说 xint 的标识符。
  • 它说 x 有外部链接(因为外部是没有存储 class 说明符的对象的默认声明,如 static 外部函数)。
  • 它定义了一个int。 (其实是暂定的定义,这里不做赘述。)

这两个声明都说 xint 的标识符并且具有外部链接。它们之间的区别是第一个没有定义对象(它只是说 x 是在其他地方定义的对象的名称),第二个确实定义了一个 int (所以它是其他地方) .所以这些声明不冲突,是允许的。

另一方面,请考虑在函数内使用这些相同的声明。然后它们在块范围内。

那么extern int x;的含义同上:x是对别处定义的对象进行外部链接的标识符。

但是int x;有不同的含义。它不是说 x 有外部(或内部)链接,而是说 x 没有链接,因为没有链接是块范围内声明的默认值。这会产生冲突,因为 C 2018 6.7 3 表示没有链接的标识符不得在同一范围内多次声明(和名称 space,此处未解决),但 typedef 名称和标签除外某些条件。

作用域

C有四种作用域:

  • 文件范围用于函数外的声明,并持续到正在编译的源文件的末尾。
  • 块作用域用于函数内部的声明并持续到块结束(在下面讨论)。
  • 函数原型范围用于函数原型参数中的声明。 (例如,在 void foo(int n, float a[n][n]);na 中具有函数原型作用域。)
  • 函数范围是在 goto 语句中使用的标签。

复合语句是 {} 中的声明和语句的列表。每个复合语句都是一个块,它为声明创建一个新的范围。一个函数的主体是一个复合语句,它是一个块,它里面可能有额外的复合语句,每个都开始一个新的作用域。

块也由 switchifdowhilefor 语句创建,但它们对于第一个语句来说基本不重要其中四个,因为只有 for 声明提供了进一步声明的机会。例如,在 for 语句中,您可以编写 for (int x = 3; x < 20; ++x),这会创建一个新的 x 实例,因为 for 语句会启动一个新块。