[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第七章. ...
随机推荐
- 简述c和c++的基本区别,你真的懂吗?(面试必学)
前言本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理.作者:angry_youth **1.c和c++的头文件不同:** c的头 ...
- 新手学习FFmpeg - 如何编写Kubernetes资源文件
Kubernetes API的使用方式 Kubernetes API属于声明式API编程, 它和常用的命令式编程有一些区别. 通俗的说,命令式编程是第一人称,我要做什么,我要怎么做. 操作系统最喜欢这 ...
- Day 10 面向对象基础
目录 面对过程编程 面向对象编程 类 定义类 对象 定义对象 定制对象独有特征 面对过程编程 分析解决问题所需要的步骤, 用函数将这些步骤一步一步实现, 使用的时候一个个调用就可以了 优点: 复杂的问 ...
- C#中关于值类型和引用类型的区别
小声哔哔 一直以来对于值类型与引用类型之间的区别都不是特别清晰,直到踩了坑.... 正好最新闲暇,便想着梳理梳理这一两年来遇到的奇葩问题和解决方案,顺便就把它给拎出来记一下,免得以后再摔跤 值类型与引 ...
- jenkins+gitlab+webhook实现自动发布
实验环境 Jenkins:192.168.1.15 Gitlab:192.168.1.14 一.Jenkins配置 1:安装gitlab hook plugin插件 2:新建一个job 3 ...
- python爬虫--selenium模块.上来自己动!
selenium 基本操作 from selenium import webdriver from time import sleep #实例化一个浏览器对象 bro = webdriver.Chro ...
- 面试题:为什么客户端最后还要等待2MSL
面试题: 为什么客户端最后还要等待2MSL MSL(Maximum Segment Lifetime),TCP允许不同的实现可以设置不同的MSL值. 保证客户端发送的最后一个ACK报文能够到达服务器, ...
- 优先队列与TopK
一.简介 前文介绍了<最大堆>的实现,本章节在最大堆的基础上实现一个简单的优先队列.优先队列的实现本身没什么难度,所以本文我们从优先队列的场景出发介绍topK问题. 后面会持续更新数据结构 ...
- Dubbo简介与基本概念
场景 分布式系统的发展演变以及RPC简介: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/103555049 Dubbo简介 Ap ...
- AI: Web: 2 Vulnhub Walkthrough
靶机下载链接: https://www.vulnhub.com/entry/ai-web-2,357 主机端口扫描: 尝试SQL注入,未发现有注入漏洞,就注册创建于一账户 http://10.10.2 ...