[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第七章. ...
随机推荐
- 搭建Squid3 密码账号IP代理
上文中,说明了 Squid3 IP Proxy 隐藏原IP,这里就搭建Squid 3密码账号IP代理进行整理,涉及环境 Ubuntu 18.04. Step 1: htpasswd 和 htdiges ...
- 如何正确的使用Python解释器?你之前肯定用错了
作为python开发者,当我们编写Python代码时,我们得到的是一个包含Python代码的以.py为扩展名的文本文件.要运行代码,就需要Python解释器去执行.py文件.由于整个Python语言从 ...
- centos7下ldap+kerberos实现单点登陆
一. LDAP概念 http://wiki.jabbercn.org/index.php/OpenLDAP2.4%E7%AE%A1%E7%90%86%E5%91%98%E6%8C%87%E5%8D%9 ...
- stm32 io操作 头文件规范
在stm32众多项目开发中,有太多的对io进行操作,若置1或清0,使用官方库提供的函数,固然方便,规范,但是需要包含标准的库,尺寸较大,还得处理不同版本兼容问题,包括io初始化也太繁琐,于是操作原子等 ...
- 如何用css画一个文件上传图案?
如下图,如果是你,你会怎么实现: 通常我们会通过字体图标来显示中间的加号,外层用一个div包裹即可:或者使用伪元素来模拟中间的一横一竖,这都比较麻烦. 其实我们可以直接使用div+css就可以实现. ...
- 深入理解 Java 反射和动态代理
- postman设置全局变量
//处理token var jsn = JSON.parse(responseBody) console.log(jsn.access_token) //把access_token设置到全局变量中 p ...
- 【Java8流】使用学习
[Java8流]使用学习 ============================================= 1.删除子目录及文件 ============================== ...
- CQRS+ES项目解析-Diary.CQRS
在<当我们在讨论CQRS时,我们在讨论些神马>中,我们讨论了当使用CQRS的过程中,需要关心的一些问题.其中与CQRS关联最为紧密的模式莫过于Event Sourcing了,CQRS与ES ...
- Android 插件化开发(四):插件化实现方案
在经过上面铺垫后,我们可以尝试整体实现一下插件化了.这里我们先介绍一下最简单的实现插件化的方案. 一.最简单的插件化实现方案 最简单的插件化实现方案,对四大组件都是适用的,技术面涉及如下: 1). 合 ...