在块中获取全局变量时,`extern` 做了什么? (使用相同名称定义的不同变量)

What does `extern` do when getting a global variable inside a block? (with a different variable defined with the same name)

最近学习了C语言的全局变量和局部变量,在网上找到了这个例子(没有明确的解释)

#include<stdio.h>

int x = 50;

int main(){
    int x = 10;
    {
      extern int x;
      printf("global: %d\n", x);
    }
    printf("local: %d\n", x);
    return 0;
}

我确实知道您可以在同一个程序中使用相同的名称创建局部变量和全局变量(并且局部变量将在该函数或块中优先使用),因此 printf("local: %d\n", x); 的结果将是是 local: 10。但是我无法理解如何在代码中使用 extern 来获取定义了局部变量的块中的全局变量。

我确实研究了 extern 是如何工作的,但我仍然无法理解:How does 'declaring' a variable(by using extern int x;) inside a block help bring up全局变量?

提前致谢

每个范围都可以隐藏来自更高级别范围的标识符。 extern 声明向编译器表明代码中的其他地方存在具有外部链接的变量 x。此声明隐藏了先前的范围变量 x 定义。

#include <stdio.h>

int x = 50;

int main(){
    int x = 10;
    printf("scope 1: %d\n", x);
    {
      extern int x;
      printf("scope 2: %d\n", x);
      {
          int x = 20;
          printf("scope 3: %d\n", x);
          {
                extern int x;
                printf("scope 4: %d\n", x);
          }
          printf("scope 3 again: %d\n", x);
      }
      printf("scope 2 again: %d\n", x);
    }
    printf("scope 1 again: %d\n", x);
    return 0;
}

https://godbolt.org/z/YPc6jenEG

Program stdout

scope 1: 10
scope 2: 50
scope 3: 20
scope 4: 50
scope 3 again: 20
scope 2 again: 50
scope 1 again: 10

更多:How do I use extern to share variables between source files?

因为 int x = 50; 出现在函数外部,它声明了一个 x 具有 外部链接 ,我将在下面讨论。

int x = 10; 出现的地方,它声明了一个新的 x,它隐藏了 int x = 50; 之前的 x。由于此声明在函数内部,因此没有链接。

extern int x; 出现的地方,它声明了第三个 x,它隐藏了 int x = 10; 的前一个 x。虽然它在函数内部,但 extern 使其具有外部链接。

第一个 x 和第三个 x 的外部链接导致编译器(或链接器)将它们解析为引用同一个对象。因此,第三个 x 与第一个 x 引用相同的对象,即使它是标识符的不同实例。 (因为第二个x没有链接,所以不是链接到同一个对象)

声明 extern int x; 引用在文件范围内声明的 int x; 这一事实是 链接 工作原理的一个示例。指定 object 或函数的标识符可能具有以下链接之一:

  • 带有外部链接的标识符在程序的任何翻译单元(即源文件和包含的headers)中表示相同的object或函数。
  • 具有内部链接的标识符在一个特定的翻译单元中表示相同的object或功能。
  • 没有链接的标识符代表不同的object并且只存在于声明它们的范围内。

在您的示例中,文件范围内的 int x; 具有外部链接,因为它未使用 static 存储 class 说明符声明。

int x;main函数的最外层声明的块没有链接,因为上面没有存储class说明符,所以这个声明定义了一个新的object 掩码 x 在文件范围内声明。

现在您在 main 内的 sub-block 中声明了 extern int x;。因为这个标识符是用 extern 声明的,所以它与先前的可见声明进行比较,特别是在它上面的块作用域中声明的 x。由于此先前声明没有链接,因此此新标识符具有外部链接(如果先前声明具有内部链接,则它将具有内部链接)。这在 C standard:

的第 6.2.2p4 节中有更详细的描述

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.

并且因为它具有外部链接,所以它引用与在文件范围内声明的 int x; 相同的 object。