文章目录
- 概述
- C语言变量四大属性
- 存储类
- 概念解析
- Linux 内存映像并解析
- 作用域
- 概念解析
- 局部变量的代码块作用域
- 函数名和全局变量的文件作用域
- 同名变量的掩蔽规则
- 生命周期
- 概念解析
- 栈变量的生命周期
- 堆变量的生命周期
- 数据段、bss段变量的生命周期
- 代码段、只读段的生命周期
- 链接属性
- 概念解析
- C语言程序的组织架构:多个C文件+多个h文件
- 编译以文件为单位、链接以工程为单位
- 三种链接属性:外链接、内链接、无链接
- 函数和全局变量的同名冲突
- static的第二种用法:修饰全局变量和函数
- 结束语
概述
整个系统(Linux系统、uboot)正常启动并长期运行是由很多变量共同作用而成的,类比我们社会的运作也是由于一代又一代的人不断辛勤努力,不断向社会发展贡献自己的价值一样。本文章从四大特征详细了解下这类在系统中不断更新换代的“人们” —— C语言变量。
C语言变量四大属性
C语言四大属性:存储类、作用域、生命周期、链接属性
存储类
概念解析
1、存储类是存储类型,描述C语言变量在何地方存储。
2、内存有多重管理方式:栈、堆、数据段、bss段、.text段
一个变量的存储类属性就是描述这个变量存储在何种内存段中。
3、局部变量分配在栈上,所以它的存储类就是栈
显示初始化为非0的全局变量分配在数据段
显示初始化为0和没有显示初始化(默认为0)的全局变量分配在bss段
Linux 内存映像并解析
Linux 内存模型
以上是Linux 系统中 内存分区的分布图,将内存地址分为不同的区域用来存储对应的数据内容,以下结合Linux内核模型分布图来了解下这些内存段以及其存储的具体内容。
内存分区 | 说明 |
---|---|
内核映射区 | 内核映射区就是将操作系统内核程序映射到这个区域了 对于linux中的每一个进程来说,它都以为整个系统中只有它自己和内核而已。它认为内存地址0xC0000000以下都是它自己的活动空间,0xC0000000以上是OS内核的活动空间。 每个进程都活在自己独立的进程空间中,0 — 3G的空间每个进程是不同的。(因为使用了虚拟地址技术),但是内核是唯一的。 |
栈 | 栈内存区存局部变量;函数调用传参过程也会用到栈 |
文件映射区 | 文件映射区就是进程打开了文件后,将文件的内容从硬盘读进进程的文件映射区,以后直接在内存中操作这个文件,读写完了后在保存时再将内存中的文件写到硬盘中去。 |
堆 | C语言是不会自动向堆中存放东西,堆的操作完全是程序员自己手工操作的。程序员根据自己的需求申请、使用、释放。 |
数据段、bss段 | 数据段就是(1)显示初始化为非0的全局变量(2)显示初始化为非0的static局部变量 bss段就是存(1)系那是初始化为0或者未显示初始化的全局变量(2)显示初始化为0或未显示初始化的static局部变量。 |
代码段、只读数据段 | 代码段就是对应程序中的代码(函数),代码段在linux中称为文本段(.text) 只读数据段就是程序运行期间只能读不能写的数据。 const修饰的常量可能是存在只读数据段的。 |
作用域
概念解析
1、作用域是描述这个变量起作用的代码范围。
2、C语言变量的作用域规则是代码块作用域。
意思是这个变量起作用的范围是当前的代码块。代码块就是一对大括号 {} 扩起来的范围,所以一个变量的作用域就是这个变量定义所在的 {} 范围内从这个变量定义开始往后的部分。
局部变量的代码块作用域
1、代码块基本可以理解为一对大括号 {} 括起来的部分。
2、代码块不等于函数,if while for 都有 大括号{}
3、局部变量的作用域是代码块作用域,也就是说一个局部变量可以被访问和使用的范围仅限于定义这个局部变量的代码块中定义式之后的部分。
函数名和全局变量的文件作用域
1、文件作用域就是全局的访问权限,也就是在整个.c文件中都可以访问。这就是平时所说的局部和全局,全局就是文件作用域
2、函数和全局变量的作用域是定义所在的整个.c文件之内定义式之后的部分。
小结:不管是局部变量、全局变量、函数,都要先定义才能使用
同名变量的掩蔽规则
编程中,不可能避免会出现同名变量。变量同名后不一定会出错。
1、如果两个同名变量作用域不同且没有交叠,这种情况下同名没有影响。
2、如果两个同名变量作用域有交叠,C语言规定在作用域交叠范围内,作用域小的一个变量会掩蔽掉作用域大的那种。
生命周期
概念解析
1、生命周期是描述这个变量什么时候诞生(运行时分配内存空间给这个变量)以及什么时候死亡(运行时收回这个内存空间,此后再不能访问这个内存地址,或者访问这个内存地址已经和这个变量无关了)。
2、研究变量的生命周期可以帮助理解程序运行的一些现象、理解C语言的一些规则。
栈变量的生命周期
1、局部变量(栈变量)存储在栈上,生命周期是临时的。临时:代码执行过程中按照需要去创建、使用、消亡的。
2、如一个函数内定义的局部变量,在这个函数每一次被调用时都会创建一次,然后使用,最后在函数返回的时候消亡。
堆变量的生命周期
1、堆内存空间是客观存在的,是由操作系统维护的。程序只是去申请然后使用然后释放。
2、我们只关心程序使用堆内存的这一段时间,所以堆变量生命周期:从malloc申请时诞生,然后使用,直到free时消亡。
3、堆内存在malloc之前和free之后不能再去访问,因此堆内存在实践编程时是被反复的使用的。
数据段、bss段变量的生命周期
1、全局变量的生命周期是永久的。永久:是在程序被执行时诞生,在程序终止时消亡。
2、全局变量所占用的内存是不能被程序自己释放的,所以程序如果申请了过多的全局变量会导致这个程序一直占用大量内存。
代码段、只读段的生命周期
代码段就是函数,它的生命周期是永久的。
代码段不只是代码,还有const类型的常量,还有字符串常量。(const类型的常量、字符常量有时候放在rodata段,有时候放在代码段,取决于平台)
链接属性
概念解析
1、链接属性描述的是程序从源代码到最终可执行程序,经历的过程:编译、链接
2。编译阶段就是把源代码搞成 .o 目标文件,目标文件里面有很多符号和代码段、数据段、bss段等分段。符号就是编程中的变量名、函数名等。运行时变量名、函数名能够和相应的内存对应起来,靠符号来做链接的。
3、 .o的目标文件链接生成最终可执行程序的时候,其实是把符号和相应的段连接起来。C语言中的符号有三种链接属性:外链接属性、内链接属性、无链接属性
C语言程序的组织架构:多个C文件+多个h文件
1、整个而又庞大的C语言程序(linux内核、uboot)由多个C文件和h文件组成的。
2、程序的生成过程:编译+链接
编译是为了将函数/变量等变量.o二进制的机器码格式
链接是为了将各个独立分开的二进制的函数链接起来形成一个整体的二进制可执行程序。
编译以文件为单位、链接以工程为单位
1、编译器工作时是将所有源文件依次读进来,单个为单位进行编译的。
2、链接的时候实际上是把第一步编译生成个单个的.o文件整体的输入,然后处理链接成一个可执行程序。
三种链接属性:外链接、内链接、无链接
1、外链接:外部链接属性可以在整个程序范围内进行链接(可以跨文件),如函数和全局变量
2、内链接:内连接属性可以在当前C文件内部范围内进行链接(不能再除当前C文件之外的其他C文件中进行访问、链接)。如static修饰的函数/全局变量属于内链接。
3、无链接:不参与链接属性就是跟链接没有关系。如所有局部变量(auto的、static的)都是无链接的。
函数和全局变量的同名冲突
1、因为函数和全局变量是外部链接属性,就是每个函数和全局变量将来在整个程序中所有的C文件都能被访问,因此在一个程序中的所有C文件中不能出现同名的函数/同名的全局变量。
2、现在高级语言中完美解决这个问题的方法是命名空间namespace(是给一个变量带上各个级别的前缀)
3、C语言发明了一种不是很完美的解决方案,是三种链接属性的方法。思路:将明显不会在其他C文件中引用(只在当前C文件中引用)的函数/全局变量,使用static修饰使其成为内链接属性,这样在链接时即使2个C文件中有重名的函数/全局变量,只要其中一个或2个为内链接属性就没事。(在一定程度上解决,没有根本解决问题)
static的第二种用法:修饰全局变量和函数
1、普通的函数/全局变量,默认链接属性是外部的。
2、static修饰的函数/全局变量,链接属性是内部链接。
结束语
本文章介绍到这里。结合本文章介绍的,从C语言变量的这四大属性入手了解程序中每一个变量,相信大家都有巨大的收获。以及可以解释相关的疑问,如:“一个函数内的局部变量为什么在函数外不能使用?” “局部变量为什么分配在栈上?或者说局部变量为什么是临时生命周期?”等等。这四大属性相互之间是独立的,对大家理解代码可能没有太大的作用,但是将它们相互关联起来威力可是巨大的。接下来出一个文章将结合本章的内容了解下存储类关键字的精彩“人生”。