链接和生存周期的区别:

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

链接:

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

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

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

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

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

函数外部使用了extern存储修饰符的函数和对象标识符(事实上这是一个不规范的写法,如果你明确这个变量需要被外部引用,最好就是不带任何存储修饰符,如果必要这样做,你需要写成这样示例3);

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

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

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

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

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

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

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

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

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

函数参数;

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

   

示例1:

  1. int func1(void);      // func1具有外部链接。
  2. int a; // a具有外部链接。
  3. extern int b = ;       // b具有外部链接。
  4. static int c;   // c具有内部链接。
  1. static int e;   // e具有内部链接。
  2.  
  3. static void func2(int d)         // func2 具有内部链接; d具有无链接。
  4. {
  5. extern int a;    // 此a和上面一样,具有外部链接。
  6. int b = ;  // 此b具有无链接,并且将上面声明的b隐藏起来
  7.  
  8. extern int c;   // 此c和上面一样,维持内部链接。
  9.  
  10. static int e; // e具有无链接,并且将上面声明的e隐藏起来。
  11.  
  12.   int f;                         // f具有无链接
  13.  
  14. }

示例2:

  1. //翻译单元A
  2. static int foo = ;
  1. //翻译单元B
  2. #include <stdio.h>
  3.  
  4. extern int foo;
  5. void main(void)
  6. {
  7. printf("%s:foo:%d\n", __func__, foo);
  8. }

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

  1. undefined reference to `foo'

示例3:

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

示例4:

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

示例5:

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

示例6:

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

生存周期:

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

生存周期 描述 情况
进程(静态)生存期

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

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

函数外声明的对象;

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

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

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

栈(自动)生存期 对象会存在到栈退出 函数自变量;

[C]链接和生存周期的更多相关文章

  1. CLR和.Net对象生存周期

    标签:GC .Net C# CLR 前言 1. 基础概念明晰 * 1.1 公告语言运行时 * 1.2 托管模块 * 1.3 对象和类型 * 1.4 垃圾回收器 2. 垃圾回收模型 * 2.1 为什么需 ...

  2. 关于extern和static关键字引出的一些关于作用域和链接属性和存储类型的问题

    在进入正题前我们必须了解一些概念: 标识符:标识符不仅仅代表着变量的名字,main()函数的main也是一个标识符,这点很重要. 存储类型:即变量的存储位置及其生存周期:静态区:分为两块 .date ...

  3. C Primer Plus之存储类、链接和内存管理

    存储时期即生存周期——变量在内存中保留的时间 变量的作用域和链接一起表明程序的哪些部分可以通过变量名来使用该变量. 注意:生存期和作用域是两个不同的概念. 作用域    作用域描述了程序中可以访问一个 ...

  4. [转载]GCC 编译使用动态链接库和静态链接库--及先后顺序----及环境变量设置总结

    来自http://blog.csdn.net/benpaobagzb/article/details/51364005 GCC 编译使用动态链接库和静态链接库 1 库的分类 根据链接时期的不同,库又有 ...

  5. C语言存储类别和链接

    目录 C语言存储类别和链接 存储类别 存储期 五种存储类别 C语言存储类别和链接 ​ 最近详细的复习C语言,看到存储类别的时候总感觉一些概念模糊不清,现在认真的梳理一下.C语言的优势之一能够让程序员恰 ...

  6. python+uwsgi导致redis无法长链接引起性能下降问题记录

    今天在部署python代码到预生产环境时,web站老是出现redis链接未初始化,无法连接到服务的提示,比对了一下开发环境与测试环境代码,完全一致,然后就是查看各种日志,排查了半天也没有查明是什么原因 ...

  7. vs2010静态链接MFC库报链接错误

    由于需要将MFC程序在其它电脑上运行,所以需要将动态链接的MFC改成静态链接,本以为很简单,没想到链接的时候出现下面的链接错误: uafxcw.lib(afxmem.obj) : error LNK2 ...

  8. Mach-O 的动态链接(Lazy Bind 机制)

    ➠更多技术干货请戳:听云博客 动态链接 要解决空间浪费和更新困难这两个问题最简单的方法就是把程序的模块相互分割开来,形成独立的文件,而不再将它们静态的链接在一起.简单地讲,就是不对那些组成程序的目标文 ...

  9. 菜鸟在C语言编译,链接时可能遇到的两个问题

    最近在看 CSAPP (Computer Systems A Programmers Perspective 2nd) 的第七章 链接.学到了点东西,跟大家分享.下文中的例子都是出自CSAPP第七章. ...

随机推荐

  1. 初探hook的键盘获取

    初探hook的键盘获取 import pyHook import pythoncom class e(): keyIsPressed = False #键盘是否按下 按住.. def onKeyDow ...

  2. ARTS-S C语言多线程传参数

    #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h& ...

  3. 【JS】307- 复习 Object.assign 原理及其实现

    点击上方"前端自习课"关注,学习起来~ }let b = {    name: "muyiy",    book: {        title: " ...

  4. 基于 .NET Core 的简单文件服务器

    Netnr.FileServer 基于 .NET Core 的简单文件服务器,数据库为SQLite 源码 https://github.com/netnr/blog https://gitee.com ...

  5. screen虚拟终端工具

    说明:有时候我们要执行一个命令或脚本,需要几小时甚至几天,但是不能中断,有时想查看当前输出信息的时候,可以将它丢到后台运行,但是后台运行却无法显示或输出相关信息出来:我们可以使用一个虚拟终端工具scr ...

  6. 索引与Order By

    Order By 将对结果进行排序,这里的排序最大的特点是资源密集型,尽管多数时候它同时也是CPU密集型的.数据库在进行排序时,必须缓冲临时结果,读取到所有输入,并在完整的排序操作后才能产生第一个输出 ...

  7. Java中Object类常用的12个方法,你用过几个?

    前言 Java 中的 Object 方法在面试中是一个非常高频的点,毕竟 Object 是所有类的“老祖宗”.Java 中所有的类都有一个共同的祖先 Object 类,子类都会继承所有 Object ...

  8. Java开发数据库设计的14个技巧,你知道几个?

    1. 原始单据与实体之间的关系 可以是一对一.一对多.多对多的关系.在一般情况下,它们是一对一的关系:即一张原始单据对应且只对应一个实体.在特殊情况下,它们可能是一对多或多对一的关系,即一张原始单证对 ...

  9. MySQL使用可重复读作为默认隔离级别的原因

    一般的DBMS系统,默认都会使用读提交(Read-Comitted,RC)作为默认隔离级别,如Oracle.SQL Server等,而MySQL却使用可重复读(Read-Repeatable,RR). ...

  10. 建议2:注意Javascript数据类型的特殊性---(4)避免误用parseInt

    parseInt是一个将字符串转换为整数得函数,与parseFloat(将字符串转换为浮点数)对应,这两种函数是JavaScript提供得两种静态函数,用于把非数字得原始值转换为数字. 在开始转换时, ...