【问题标题】:c - static and extern one againc - 再次静态和外部
【发布时间】:2023-09-18 02:59:01
【问题描述】:

我正在检查具有不同链接的声明中可能发生的各种情况,我有 2 个问题。

我。我有以下代码:

#include <stdio.h>  

static int a = 19;
         
   int main(void)
   {
      extern int a;
      {
          extern int a;
          printf("braces - %d\n", a);
      }   
      printf("main - %d\n", a);
  }

它与clanggcc 都可以很好地编译,19 的相同结果会在pritfs 中打印。据我所知,所有as 都是static,根据标准的 6.2.2.、4)。我唯一不明白的是,为什么文件范围 a 在 main 和大括号中对 a 可见?不应该按照脚注 31 隐藏文件范围吗?当我在其他文件中用不同的值定义另一个a 时,printfs` 的输出仍然是 19。

二。现在我执行以下操作:

#include <stdio.h>  

static int a = 19;
         
   int main(void)
   {
      int a;                           //change in this line
      {
          extern int a;
          printf("braces - %d\n", a);
      }   
      printf("main - %d\n", a);
  }

gcc 大喊variable previously declared ‘static’ redeclared ‘extern’,而clang 表现正常并在main 中为a 打印a = 0(是的,它是垃圾值)并且在大括号中仍然是19。
我猜gcc 适用于此 6.2.2。 7) 的标准,而clang 没有。哪个解释是正确的,这里发生了什么?

我只能假设 gcc ''matches'' a 在大括号中与 a 在 main 中(没有链接)并使其成为外部链接,然后发现它与文件范围 @ 冲突987654344@。而且,为什么不在另一个文件中使用大括号 a 引用 a(脚注 31?)?


事实上,我目前的理解接近于here 中接受的答案中的理解,尽管我确实理解 C++ 与 C 有不同(我问的是 C)。

【问题讨论】:

    标签: c scope static extern linkage


    【解决方案1】:

    我唯一不明白的是为什么文件范围是 a in main 和大括号是否可见?

    文件范围内声明的变量a在main的块范围内可见

       static int a = 19;
             
       int main(void)
       {
          extern int a;
          //...
    

    直到 main 中的声明重新声明了文件范围内的变量。所以 main 中的变量表示文件范围中声明的相同变量。

    在复合语句的块范围内

    int main(void)
    {
       extern int a;
       {
           extern int a;
           //...
    

    在 main 的封闭范围内声明变量 a 是可见的,直到在复合语句的块范围内声明变量 a。因此,复合语句中声明的变量表示在函数 main 的封闭块范围内声明的相同变量。所有三个变量都表示具有内部链接的同一个变量。

    至于第二个程序则根据 C 标准(6.2.2 标识符链接)

    7 如果在一个翻译单元中,相同的标识符出现在两个 内外联动,行为未定义

    程序具有未定义的行为,因为在函数 main 的块范围内声明的变量

    static int a = 19;
         
    int main(void)
    {
        int a; 
        // ...
    

    通过内部链接隐藏在文件范围内声明的变量。所以根据 C 标准(6.2.2 标识符的链接)

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

    复合语句块范围内声明的变量a具有外部链接。因此,翻译单元中的相同标识符具有外部和内部链接。

    【讨论】:

    • 谢谢同志的回答!我猜是6.2.1。 4)从我的理解中溜走。隐藏仅在标识符指定不同实体时发生,但在第一种情况下,所有三个都指定一个实体。
    • @AntonTretyakov 你是对的。在第二个程序中,没有存储说明符 extern 的变量 a 的声明隐藏了在文件范围内声明的带有链接的 a 声明。
    • 短语“在该标识符的先前声明可见的范围内......”似乎有点笨拙和模棱两可。在正常的英语用法中,说 X 在 Y 中可见通常意味着在 Y 内有一个 X,从某个位置可见(根据上下文,可能在或不在 Y 内),而不是意思宇宙中某处有一个 X 是可见的从内部 Y。我认为标准的意思是说新标识符被声明为“当先前声明在范围内时”并不明确, 如果事先声明不是...
    • @supercat 考虑以下程序 int x; int main( void ) { /* 这里是 x 的声明是可见的*/ { /* 在这个代码块中声明仍然可见*/ { /* 在这个代码块中声明是可见的*/ } } } 直到标识符将在没有存储说明符 extern 的情况下重新声明,它将在每个嵌套的声明区域中可见。
    • ...包含在与新版本相同的范围内。我认为将编译单元包含相同标识符的内部和外部定义的场景分类为 UB 旨在允许实现处理包含在函数中但共享名称的 extern文件范围static 对象,被视为对文件范围对象的引用或与其分开的导入引用。