存储类,作用域,生命周期和链接属性
Posted 阿C_C
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了存储类,作用域,生命周期和链接属性相关的知识,希望对你有一定的参考价值。
概念解析
存储类
存储类就是存储类型,描述C语言变量在什么地方存储,内存中有多重管理方法,堆,栈,数据段,bss段,text段,变量存储类属性描述的就是这个变量存储于什么内存段中。
作用域
作用域就是该变量起作用的范围,C语言变量起作用的范围是当前代码块。
生命周期
生命周期描述的是运行时给变量分配内存空间,以及回收该变量的内存空间的一个阶段。
链接属性
把源代码编译成目标文件之后,目标文件总包含众多的符号和各种段,运行时函数名,变量名需要和内存对应起来,这要靠符号做链接,将目标文件链接生成最终的可执行程序的时候,就是把符号和对应的段链接起来了,C语言中有三种链接属性:外链接,内链接和无连接。
内存映像
代码段,只读数据段
代码段在linux中又叫做文本段,名称为.text,只读数据段存储的是在程序运行期间只能读不能写的数据
数据段,bss段
数据段主要存储,显示初始化为非0的全局变量,显式初始化为非0的静态局部变量。bss段主要存储,显示或者默认初始化为0的全局变量,
堆内存
堆内存需要程序员自己手动操作,用的时候申请,使用完毕之后需要手动释放。
文件映射去
文件映射区就是进程打开文件之后,将文件的内容从硬盘读取到这个文件映射区中,以后直接在该映射区中操作该文件,操作完毕之后,再将映射区中的内容写入到硬盘的文件中。
栈
局部变量会被分配到栈上,函数调用传参过程中,也会使用到栈。
内核映射区
将操作系统内核程序映射到这个区域,linux中的每个进程,都以为整个系统只有程序自身和内核而已,进程认为内存0xc0000000以下都是自身的活动空间,以上的是操作系统内核的空间,使用虚拟地址映射,每个进程都在自己独立的内存空间中,都认为自身都掌握着0-3G这段内存空间,但是,所有的进程看到的内核都是同一个内核。
OS程序和裸机程序的差异
C语言的程序运行时对环境有一定的要求,单独写的C语言程序无法直接在内存中运行,需要通过外部的协助,这些协助的代码称为构建C语言运行时环境的代码。
OS程序
在操作系统下这些代码已经写好并且会自动我们写的C语言程序中,主要作用是:赋值全局变量,清理bss段等。这些操作在我们的程序启动时都会自动执行。
裸机程序
在裸机程序中,没人帮我们加载这一段环境构建代码,需要我们手动自己去做。
存储类
auto
auto在C语言中只能修饰局部变量,被auto修饰的局部变量,会被分配到栈上,auto关键字可以省略,我们平时定义的局部变量自动就是auto修饰的,只不过我们在写法上省略了而已。
static
static在C语言中有两种用法,这两种用法彼此之间没有关系,是独立的,第一种用法是用于修饰局部变量,形成静态局部变量,第二种用于修饰全局变量,形成静态全局变量,这两种含义是完全不同的,静态全局变量和非静态全局变量区别在于链接属性不同。
static修饰局部变量时,存储类发生了变化,非静态局部变量分配在栈上,但是静态局部变量根据初始化值分配在了数据段或者bss段,这些都和全局变量非常类似,区别在于作用域和链接属性不同,静态局部变量作用域是当前代码块,链接属性是无连接,全局变量作用域是当前文件,链接属性为外连接。
register
register修饰的变量,编译器会尽量将其分配在寄存器中,可以提高读写效率,常用于变量会被反复高频率使用的场景,可以极大提升程序的运行效率。编译器只是承诺尽量会被分配在寄存器中,但是不能保证。
extern
extern主要用于声明全局变量,这种变量可以在另外一个源文件中被访问到,C语言中程序的编译是以源文件为单位的,每个文件在编译时根本不会考虑其他文件中的内容。其他文件中要使用extern变量时,需要先声明,才能使用。
volatile
表示一个变量可以在编译器之外改变,编译器在编译当前代码时无法预知的改变,就叫做编译器之外的改变,编译器在遇到volatile修饰的变量时,就不会对该变量的访问进行优化,就不会出现错误。
restrict
在C99中加入的关键字,只用于限定指针以及指针访问,该关键字告知编译器,所有修改该指针所指向内容的操作,只能通过该指针进行,不存在其他途径,以帮助编译器进行代码优化,生成更有效率的汇编代码。
作用域
作用域表示变量在什么范围内起作用。
局部变量的作用域
局部变量的作用域是在代码块当中,代码块就是一对””括起来的部分,仅限于定义这个局部变量的代码块之后的部分。
函数和全局变量的作用域
函数和全局变量的作用域,是整个源代码文件,具体说是在整个源码文件定义了函数或者全局变量之后的范围,因为定义之前由于缺少声明所以没法使用
同名变量的掩蔽规则
编程的时候不可避免会出现同名变量,变量同名后不一定会出错,如果两个变量的作用域不同且没有交叠,则没有任何影响,如果作用域有交叠,则C语言规定,在交叠范围内,作用域小的变量会掩蔽作用域大的变量。
生命周期
掌握变量的生命周期有助于理解变量的行为特征。
栈变量的生命周期
存储在栈上的都是局部变量,生命周期是临时的,在代码执行过程中按照需要创建,使用和消亡。
堆变量的生命周期
堆内存空间是客观存在的,由操作系统维护,由程序去申请,使用完毕之后释放。堆变量从申请时诞生,使用,直到调用free的时候消亡,所以说堆内存的生命周期就是在mallo和free之间。
数据段,bss段的生命周期
全局变量的生命周期是永久的,在程序执行时诞生,在程序结束时消亡。全局变量所占用的内存不能被程序自动释放,所以说如果程序申请了过多的全局变量,会导致程序占用了大量的内存空间。
代码段,只读段的生命周期
代码段和只读段的生命周期和全局变量一样是永久的,代码段,只读段中存储的是函数,还可能有const类型的常量和字符串常量,这些的生命周期一般我们不会过多关心。
链接属性
C语言程序是由多个源文件和多个头文件组成的,程序的生成过程就是将这些文件编译和链接起来。编译是为了将函数,变量等转化为.o机器码,链接是为了将各个独立的机器码函数链接起来形成一个整体的二进制可执行程序。
编译器工作时,以文件为单位依次读入文件,链接的时候,把编译生成的.o文件输入,链接生成一个可执行程序。C语言中有三种链接属性:
- 外链接,可以在整个程序范围内进行连接,可以在整个程序的范围内找到,并进行连接,例如函数和全局变量,可以跨文件查找和链接
- 内链接,指的是可以在目标文件内部进行链接和查找,不能在文件外部查找连接,例如static修饰的函数或者全局变量
- 无链接,表示该符号不参与连接,例如局部变量
函数和全局变量同名冲突
由于函数和全局变量都属于外链接属性,因此在一个程序的所有源文件中,不能出现同名的全局变量或者函数,为了解决这种冲突,我们可以把明确不会被其他文件引用的函数或者全局变量使用static修饰为内链接属性,这样就不会被外部链接到了。
以上是关于存储类,作用域,生命周期和链接属性的主要内容,如果未能解决你的问题,请参考以下文章