【问题标题】:Linking against a local `extern` variable链接到本地​​“外部”变量
【发布时间】:2017-11-13 18:30:50
【问题描述】:

我想这可能以前已经回答过了。如果有请标记为重复!

我无法理解 extern 对局部变量(或局部函数!)的含义。我似乎看不出它在功能上与 static 局部变量有何不同...例如:

int foo(void) {
   extern int i;
   return i++;
}

int bar(void) {
   static int i;
   return i++;
}

AFAICT,foobar 是相同的。根据 C99 规范,与 bar 中的 i 相比,foo 中的 i 应该具有外部链接。也就是说,我想不出任何方法来利用该外部链接 - 因为它是一个局部变量,所以在 foo 之外无法访问它,更不用说在文件之外了。

我确定我在这里遗漏了一些东西 - 它是什么?

编辑

感谢所有指出我在 externstatic 上缺少类型的人。你说得很对。作为 SO 的常见回答者,我现在理解在这种疏忽之后一连串反对票的挫败感。

【问题讨论】:

  • foo 只有i 的声明。所以它必须在某个带有外部链接的地方定义。就像foo 访问i 一样,其他一些函数也可以访问i
  • 在这种情况下,extern i; 只是一个声明而不是一个定义。这不会编译。 rextester.com/AMSELK81614
  • @Stargateur:如果i 声明了一个类型,它确实会编译。如果没有另一个包含i定义 具有外部链接和相同类型的翻译单元,它根本无法构建有效的程序。
  • 你指定了c99标签,但你使用隐式int?
  • extern i;static i; 在现代 C 语言中都无效,从 C99 开始。并且您标记了您的问题 [c99]。

标签: c static c99 extern


【解决方案1】:

对于初学者来说,这个函数定义和变量声明

int foo() {
       ^^^
   extern i;
   ^^^^^^^^^
   return i++;
}

无效,因为缺少变量i 的参数列表和类型说明符。

应该这样写

int foo( void ) {
         ^^^^
   extern int i;
   ^^^^^^^^^
   return i++;
}

根据 C 标准(6.2.2 标识符的链接)

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

所以函数中声明的变量i没有必要有外部链接。例如,在这个演示程序中,函数内声明的变量i 具有内部链接。

#include <stdio.h>

static int i;

int foo( void ) 
{
    extern int i;
    return i++;
}

int main(void) 
{
    printf( "f() = %d\n", foo() );
    printf( "i = %d\n", i );

    return 0;
}

在函数bar 中应该这样定义

int bar( void ) {
   static int i;
   return i++;
}

变量i 没有链接,但它具有静态存储持续时间。

【讨论】:

    【解决方案2】:
    int foo() {
       extern i;
       return i++;
    }
    

    在这里,您使用解释器查看 i 在某个外部块中的某处声明,并将此对象与之前在某处声明的对象 i 连接 - 在您的情况下,在文件范围内。

    在第二种情况下,您告诉解释器将变量 i 存储在某个永久存储中,该存储在整个执行过程中都可用,并且对于函数来说是本地的。

    链接的算法在 ISO 9899 中的paragraph 6.2.2--Linkage of identifiers. 中定义——它的一部分:

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

    另见here

    外部链接:带有外部链接的标识符代表整个程序中的相同对象或函数,即在属于该程序的所有编译单元和库中。标识符可用于链接器。当第二次声明具有外部链接的相同标识符时,链接器会将标识符与相同的对象或函数相关联。

    【讨论】:

    • 好的,那么extern i; 内部foo 和 foo 外部之间有什么区别吗?
    • 哦,没关系。当然有。 i 可以在被链接的东西中定义。 extern i; inside foo 表示 i 只能在 foo 内访问。
    • 因此无法将foo中的ii中的bar连接起来,但是如果您在其他函数extern i中使用extern i,则在某些情况下没有预先声明块,来自fooi 将与来自xxxi 是同一个对象。这种情况下的定义必须在文件范围内。
    • 同意该评论的第一部分。但是,确定extern i 在某些块范围内的定义可以来自文件范围之外吗?我的意思是,它可能来自您链接的某个库,对吗?
    • @Alec 唯一的区别是所有其他想要使用i 的函数都需要复制声明。这就是为什么你永远不会在函数范围内看到 extern 声明的原因。而且,事实上,extern 声明应该在头文件中,以便编译器可以检查 declarationdefinition .
    【解决方案3】:

    Extern : extern 变量有 external links ,extern 变量的主要用途是链接其他文件中的变量,所以你说“因为它是本地的变量,它不能在 foo 之外访问”这是错误的,检查下面的代码。

        xyz@xyz-PC:~/s_flow/alec$ vi one.c
        #include<stdio.h>
        int foo() {
                extern i;//this is just declaration, not definition
                return i++;
        }
        main()
        {
                int ret = foo();
                printf("ret = %d \n",ret);
        }
    

    现在linker 将尝试在other file 或以下函数中找到definition of "i",如果找不到链接器将抛出错误。当你编译上面的代码时,你会得到链接器错误。所以在其他文件中提供定义为

    xyz@xyz-PC:~/s_flow/alec$ vi two.c
    i=100;//this is definition of i
    

    现在像gcc one.c two.c 一样编译并检查

    static :具有internal linkage 的静态存储类,这意味着您不能在该文件(在其中声明它)之外使用静态变量。

         xyz@xyz-PC:~/s_flow/alec$ vi one.c
         main()
         {
              printf("i = %d \n",i);
         }
    

    现在如果我试图访问在 two.c 到 one.c 中定义的静态变量,它会抛出错误。

    xyz@xyz-PC:~/s_flow/alec$ vi two.c
    void bar() {
       static i = 10;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-03
      • 2018-03-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多