zoukankan      html  css  js  c++  java
  • [C]链接和生存周期

    链接和生存周期的区别:

    • 链接是标识符的属性;
    • 生存周期是对象的属性;
    • 链接可以是外部(external),内部(internal)或没有(none);
    • 生存周期可以是自动的、静态的,或已分配的(allocated);

    链接:

    一个被声明在多个翻译单元内的标识符,或者在同一个翻译单元内被声明多次的标识符,可以每次指向相同的对象或函数。如示例4,具有外部链接的变量,引用的都是同一个对象;

    如果该标识符具有链接,那么它和其他跟它一样具有相同链接的标识符共享一个对象或函数;

    只有对象和函数标识符可以有外部或内部链接!所以一些预处理指令不能跨编译单元访问;

    概念 概念  属于该状态的声明 例外 关于隐藏
     外部链接  具有外部链接的标识符,表示整个程序内都是相同的函数或对象。编译器会将这种标识符交给连接器(linker),由链接器解析(resolve)这类标识符,并且把其他翻译单元和链接库中的相同标识符链接起来。

    函数外部使用了不带存储修饰符的函数和对象标识符;

    函数外部使用了extern存储修饰符的函数和对象标识符(事实上这是一个不规范的写法,如果你明确这个变量需要被外部引用,最好就是不带任何存储修饰符,如果必要这样做,你需要写成这样示例3,造成这一现象的原因是,你不能把extern关键字理解成“把这个变量设置成外部链接”,而是要理解成“声明这个变量在本作用域的外层有定义,告诉编译器链接到外层声明的对象中去(如果外层变量是内部链接,那么它就是内部链接的,反之外部链接亦然)”,参考《C语言核心技术》对extern存储类修饰符的解析);

    函数内部带extern的对象,而这个对象在外部具有外部链接;

    如果标识符已经被声明为内部链接,在第一次声明的作用域内做第二次声明,并无法将此标识符的链接变成外部链接,这种情况处在翻译单元内和处在翻译单元外的表现又有不同:

    • 处在不同的翻译单元,那么链接时则报错(示例2);
    • 处在同一个翻译单元,则沿用内部链接(示例1);
    倘若其他翻译单元中定义了一个外部链接,那么就不能在 此单元内再定义一个外部链接,否则在编译器链接单元阶段会报错;
     内部链接

    “内部链接的标识符”代表在此翻译单元内是“相同的对象或函数” 。此标识符不会被链接器(linker)处理。因此,你无法在别的翻译单元中使用此标识符,以指向到相同的对象或函数。

    具有内部链接的标识符,不会和其他翻译单元内类似的标识符产生冲突(示例5)。然而,如果某个标识符在某翻译单元内具有外部链接,你就无法在该翻译单元内声明此标识符是内部链接的。或者,换句话说,如果在某个翻译单元内声明某个标识符是内部链接,就无法声明和使用另一个翻译单元中“同名称”的外部标识符。

     非函数内部,使用static存储修饰符声明的函数或对象;

     函数内部带extern的对象,而这个对象在外部具有内部链接(参考 示例1 的变量e);

      在其他翻译单元具有外部链接的情况下在此单元声明一个内部链接,则此单元隐藏外部链接,在此单元内使用内部链接(示例6
     无链接 如果标识符不是外部链接,也不是内部链接,它就是无链接的。每出现这种标识符的声明,就会多出一个新的实体(entity),也就是编译器需要在内存上创建一个单独的对象仅供此标识符使用。

     不是变量名称,也不是函数名称的标识符,比如卷标名称(label name)、结构小标签(tag)和typedef名称;

     函数参数;

     被声明在函数内,并且没有extern修饰符的对象标识符(包含被声明为static的标识符);

       

    示例1:

    int func1(void);                 // func1具有外部链接。
    int a;                           // a具有外部链接。
    extern int b = 1;                // b具有外部链接。
    static int c;                    // c具有内部链接。
    static int e;                    // e具有内部链接。
    static void func2(int d)         // func2 具有内部链接; d具有无链接。 { extern int a;    // 此a和上面一样,具有外部链接。 int b = 2;  // 此b具有无链接,并且将上面声明的b隐藏起来 extern int c;   // 此c和上面一样,维持内部链接。 static int e; // e具有无链接,并且将上面声明的e隐藏起来。

      int f;                         // f具有无链接
    }

    示例2:

    //翻译单元A
    static int foo = 1024;
    //翻译单元B
    #include <stdio.h>
    
    extern int foo;
    void main(void)
    {
        printf("%s:foo:%d
    ", __func__, foo);
    }

    在链接两个编译单元的时候,编译器会抛出错误:

    undefined reference to `foo'

     示例3:

    //直接初始化带extern的对象,编译器会发出警告
    extern int foo = 1024;
    //this is recommend.
    extern int foo;
    int foo = 1024;

     示例4:

    //翻译单元A声明变量foo
    int foo = 1024;
    //翻译单元B定义一个修改foo的函数
    void func1(void)
    {
        extern int foo;
        foo = 2048;
    }
    //翻译单元C调用函数修改了foo,并且输出
    #include <stdio.h>
    
    extern void func1(void);
    
    void main(void)
    {
        func1();
        extern int foo;
        printf("%s:foo:%d
    ", __func__, foo);//输出2048
    }

     示例5:

    //翻译单元A声明一个具有内部链接的foo
    static int foo = 1024;
    //翻译单元B声明一个具有外部链接的foo,并且让翻译单元C修改这个foo
    #include <stdio.h>
    
    extern void func1(void);
    int foo = 1;
    
    void main(void)
    {
        func1();
        printf("%s:foo:%d
    ", __func__, foo);//输出2048
    }
    //翻译单元C声明一个修改外部foo的函数
    void func1(void)
    {
        extern int foo;
        foo = 2048;
    }

    示例6:

    //翻译单元A声明一个外部链接
    int foo = 1024;
    //翻译单元B
    #include <stdio.h> void func1(void); void func2(void); static int foo = 1; void main(void) { printf("%s:foo:%d ", __func__, foo); //输出1 func1(); printf("%s:foo:%d ", __func__, foo); //输出2048 func2(); //函数内输出1024,即单元以外的foo还是沿用外部链接那个foo } void func1(void) { extern int foo; //foo具有翻译单元作用域,所以就算这里省略掉extern int foo;声明,也可以正常访问到foo foo = 2048; }
    //翻译单元C
    #include <stdio.h>
    
    void func2(void)
    {
        extern int foo;
        printf("%d
    ", foo);
    }

    示例7:

    //翻译单元A
    #include <stdio.h>
    
    int foo = 1024;
    
    void echofoo(void)
    {
        extern int foo;
        printf("A foo = %d
    ", foo);
    }
    //翻译单元B
    #include <stdio.h>
    
    extern void echofoo(void);
    extern void chfoo(void);
    
    static int foo = 2048;
    
    int main(void)
    {
        extern int foo;
        printf("B foo = %d
    ", foo);
        chfoo();
        echofoo();
    }
    //翻译单元C
    void chfoo(void)
    {
        extern int foo;
        foo = 4096;
    }

    输出:

    B foo = 2048
    A foo = 4096

    生存周期:

    对于理解对象生存周期则简单许多,只需要记住以下几点:

    名称 属于该周期的情况 生存周期 情况
    进程(静态)生存期 具有内部或外部链接的静态变量

    对象会一直存在,直至进程结束;

    brk()划分的空间直到手动调用调用free()才会释放,否则直至进程结束;

    函数外声明的对象;

    函数内带static关键字的变量;

    函数内带内/外部链接的变量;

    通过brk()划分的内存空间,例如malloc()、calloc()、realloc();

    栈(自动)生存期 无链接的函数内部变量 对象会存在到栈退出

    函数参数变量;

    函数自变量;

     

  • 相关阅读:
    7.16,7.18练习题
    Summer training(一)
    Correct Solution?
    [欢迎来怼] 团队第一周贡献分分配结果
    欢迎来怼—选题展示
    视频展示
    美工+文案展示
    作业要求20171015贡献分分配规则
    作业要求20170928-4 每周例行报告
    作业要求20170928-3 四则运算试题生成
  • 原文地址:https://www.cnblogs.com/yiyide266/p/11944429.html
Copyright © 2011-2022 走看看