[C]链接和生存周期
链接和生存周期的区别:
- 链接是标识符的属性;
- 生存周期是对象的属性;
- 链接可以是外部(external),内部(internal)或没有(none);
- 生存周期可以是自动的、静态的,或已分配的(allocated);
链接:
一个被声明在多个翻译单元内的标识符,或者在同一个翻译单元内被声明多次的标识符,可以每次指向相同的对象或函数。如示例4,具有外部链接的变量,引用的都是同一个对象;
如果该标识符具有链接,那么它和其他跟它一样具有相同链接的标识符共享一个对象或函数;
只有对象和函数标识符可以有外部或内部链接!所以一些预处理指令不能跨编译单元访问;
| 概念 | 概念 | 属于该状态的声明 | 例外 | 关于隐藏 |
| 外部链接 | 具有外部链接的标识符,表示整个程序内都是相同的函数或对象。编译器会将这种标识符交给连接器(linker),由链接器解析(resolve)这类标识符,并且把其他翻译单元和链接库中的相同标识符链接起来。 |
函数外部使用了不带存储修饰符的函数和对象标识符; 函数外部使用了extern存储修饰符的函数和对象标识符(事实上这是一个不规范的写法,如果你明确这个变量需要被外部引用,最好就是不带任何存储修饰符,如果必要这样做,你需要写成这样示例3); 函数内部带extern的对象,而这个对象在外部具有外部链接; |
如果标识符已经被声明为内部链接,在第一次声明的作用域内做第二次声明,并无法将此标识符的链接变成外部链接,这种情况处在翻译单元内和处在翻译单元外的表现又有不同: |
倘若其他翻译单元中定义了一个外部链接,那么就不能在 此单元内再定义一个外部链接,否则在编译器链接单元阶段会报错; |
| 内部链接 |
“内部链接的标识符”代表在此翻译单元内是“相同的对象或函数” 。此标识符不会被链接器(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 = ; // b具有外部链接。
static int c; // c具有内部链接。
static int e; // e具有内部链接。 static void func2(int d) // func2 具有内部链接; d具有无链接。
{
extern int a; // 此a和上面一样,具有外部链接。
int b = ; // 此b具有无链接,并且将上面声明的b隐藏起来 extern int c; // 此c和上面一样,维持内部链接。 static int e; // e具有无链接,并且将上面声明的e隐藏起来。 int f; // f具有无链接 }
示例2:
//翻译单元A
static int foo = ;
//翻译单元B
#include <stdio.h> extern int foo;
void main(void)
{
printf("%s:foo:%d\n", __func__, foo);
}
在链接两个编译单元的时候,编译器会抛出错误:
undefined reference to `foo'
示例3:
//直接初始化带extern的对象,编译器会发出警告
extern int foo = ;
//this is recommend.
extern int foo;
int foo = ;
示例4:
//翻译单元A声明变量foo
int foo = ;
//翻译单元B定义一个修改foo的函数
void func1(void)
{
extern int foo;
foo = ;
}
//翻译单元C调用函数修改了foo,并且输出
#include <stdio.h> extern void func1(void); void main(void)
{
func1();
extern int foo;
printf("%s:foo:%d\n", __func__, foo);//输出2048
}
示例5:
//翻译单元A声明一个具有内部链接的foo
static int foo = ;
//翻译单元B声明一个具有外部链接的foo,并且让翻译单元C修改这个foo
#include <stdio.h> extern void func1(void);
int foo = ; void main(void)
{
func1();
printf("%s:foo:%d\n", __func__, foo);//输出2048
}
//翻译单元C声明一个修改外部foo的函数
void func1(void)
{
extern int foo;
foo = ;
}
示例6:
//翻译单元A声明一个外部链接
int foo = ;
//翻译单元B
#include <stdio.h> void func1(void);
void func2(void);
static int foo = ; void main(void)
{
printf("%s:foo:%d\n", __func__, foo); //输出1
func1();
printf("%s:foo:%d\n", __func__, foo); //输出2048
func2(); //函数内输出1024,即单元以外的foo还是沿用外部链接那个foo
} void func1(void)
{
extern int foo; //foo具有翻译单元作用域,所以就算这里省略掉extern int foo;声明,也可以正常访问到foo
foo = ;
}
//翻译单元C
#include <stdio.h> void func2(void)
{
extern int foo;
printf("%d\n", foo);
}
生存周期:
对于理解对象生存周期则简单许多,只需要记住以下几点:
| 生存周期 | 描述 | 情况 |
| 进程(静态)生存期 |
对象会一直存在,直至进程结束; brk()划分的空间直到手动调用调用free()才会释放,否则直至进程结束; |
函数外声明的对象; 函数内带static关键字的变量; 函数内带内/外部链接的变量; 通过brk()划分的内存空间,例如malloc()、calloc()、realloc(); |
| 栈(自动)生存期 | 对象会存在到栈退出 | 函数自变量; |
[C]链接和生存周期的更多相关文章
- CLR和.Net对象生存周期
标签:GC .Net C# CLR 前言 1. 基础概念明晰 * 1.1 公告语言运行时 * 1.2 托管模块 * 1.3 对象和类型 * 1.4 垃圾回收器 2. 垃圾回收模型 * 2.1 为什么需 ...
- 关于extern和static关键字引出的一些关于作用域和链接属性和存储类型的问题
在进入正题前我们必须了解一些概念: 标识符:标识符不仅仅代表着变量的名字,main()函数的main也是一个标识符,这点很重要. 存储类型:即变量的存储位置及其生存周期:静态区:分为两块 .date ...
- C Primer Plus之存储类、链接和内存管理
存储时期即生存周期——变量在内存中保留的时间 变量的作用域和链接一起表明程序的哪些部分可以通过变量名来使用该变量. 注意:生存期和作用域是两个不同的概念. 作用域 作用域描述了程序中可以访问一个 ...
- [转载]GCC 编译使用动态链接库和静态链接库--及先后顺序----及环境变量设置总结
来自http://blog.csdn.net/benpaobagzb/article/details/51364005 GCC 编译使用动态链接库和静态链接库 1 库的分类 根据链接时期的不同,库又有 ...
- C语言存储类别和链接
目录 C语言存储类别和链接 存储类别 存储期 五种存储类别 C语言存储类别和链接 最近详细的复习C语言,看到存储类别的时候总感觉一些概念模糊不清,现在认真的梳理一下.C语言的优势之一能够让程序员恰 ...
- python+uwsgi导致redis无法长链接引起性能下降问题记录
今天在部署python代码到预生产环境时,web站老是出现redis链接未初始化,无法连接到服务的提示,比对了一下开发环境与测试环境代码,完全一致,然后就是查看各种日志,排查了半天也没有查明是什么原因 ...
- vs2010静态链接MFC库报链接错误
由于需要将MFC程序在其它电脑上运行,所以需要将动态链接的MFC改成静态链接,本以为很简单,没想到链接的时候出现下面的链接错误: uafxcw.lib(afxmem.obj) : error LNK2 ...
- Mach-O 的动态链接(Lazy Bind 机制)
➠更多技术干货请戳:听云博客 动态链接 要解决空间浪费和更新困难这两个问题最简单的方法就是把程序的模块相互分割开来,形成独立的文件,而不再将它们静态的链接在一起.简单地讲,就是不对那些组成程序的目标文 ...
- 菜鸟在C语言编译,链接时可能遇到的两个问题
最近在看 CSAPP (Computer Systems A Programmers Perspective 2nd) 的第七章 链接.学到了点东西,跟大家分享.下文中的例子都是出自CSAPP第七章. ...
随机推荐
- 【Web技术】337- 秒懂 Web 缓存
点击上方"前端自习课"关注,学习起来~ 最近把前端缓存重新整理了一下,从整体的层面上把前端所有能用的缓存方案梳理了一遍.同时,对于http缓存,使用了表格的方案,使得原先晦涩难记的 ...
- 浅谈Java三大特性
Java三大特性想必大家都不陌生:封装.继承以及多态.很多刚接触Java的小伙伴都会想,它们到底有什么了不得之处,又赋予Java这门编程语言什么魔力呢?今天我们来探讨一下吧~~ 首先,名词解释: 封装 ...
- 大数据学习笔记——Java篇之集合框架(ArrayList)
Java集合框架学习笔记 1. Java集合框架中各接口或子类的继承以及实现关系图: 2. 数组和集合类的区别整理: 数组: 1. 长度是固定的 2. 既可以存放基本数据类型又可以存放引用数据类型 3 ...
- Python之如何修改运行的快捷键
如果你在Pycharm中运行程序使用Ctrl+shift+F10快捷键,运行失败,使用Pycharm工具组,右键一下选择“Run+文件名称AAA”运行程序,直接运行成功的话,那么你就可以 更换自己的运 ...
- .Net C# 时间戳时间转换
闲话不多说,直接上代码 /// <summary> /// 时间拓展 /// </summary> public static class DateTimeExtension ...
- Python面向对象-枚举类型enum
枚举类型:在实际问题中,有些变量的值被限定在一个有限的范围内.例如:一个星期有且只有7天,一年有且只有十二个月,一个班每周有6门课程等等.如果把这些量说明为整型.字符串或者其他类型显然是不合适.编程界 ...
- Supermap/Cesium 开发心得----获取三维视角的四至范围
网上目前有两种获取当前Camera的四至范围的方法 方法一 这种方法是最通用的,即使在哥伦布视角(2.5D下依旧能准确获取值) function getCurrentExtent() { // ...
- 「SAP技术」SAP MM 明明有维护源清单,还是不能下PO?
SAP MM 明明有维护源清单,还是不能下PO? 下午收到用户报错说,创建采购订单失败,报错 :Material ### not included in source list despite sou ...
- PHP 正则匹配h1的数据报错 preg_match(): Unknown modifier 'h' in
问题: $str = "<h1>this is test msg</h1>"; $ruler = "/^<h1>(.*?)</h ...
- CentOS 线上搭建 jupyter_server 笔记
一.背景 为公司负责 Data Science 的同事配置线上 jupyter_server (jupyter + jupyter_kernel_gateway)环境. 二.环境 CentOS 7.6 ...