1.概念解析
- 存储类:描述这个变量在何种地方存储
- 作用域:描述这个变量起作用的代码范围
- 生命周期:描述这个变量什么时候诞生及什么时候死亡
- 链接属性:描述这个变量(函数)能如何被链接
- 这四个概念从不同角度分析了c语言的不同规则
2.存储类
- 代码段:又叫文本段(.text)用存放程序中的代码(函数)
- 数据段:用来存储初始化为非0的全局变量、static修饰的局部变量
- bss段:用来存储初始化为0以及未初始化值的全局变量、static修饰的局部变量
- 堆内存:程序员手动申请,并存放数据到堆中
- 文件映射区(内核程序、裸机程序一般没这个概念):用来存储打开的文件
- 栈:用来存储普通的局部变量,以及函数调用传参
3.存储类关键字
- auto:auto只有一个作用,就是修饰局部变量。其实平时的局部变量本来就是auto的,只是省略了auto而已。说到底auto加不加效果都一样
- static:static有两种用法,而且两种用法没有任何关联
- 第一种用法是修饰局部变量,使其成为“静态全局变量”,作用是分配到了数据段/bss段,生命周期变永久罢了。实际效果就是一个函数被反复调用时,里面的局部变量值是有记忆的
- 第二种用法是修饰全局变量和函数,效果是修改链接属性为内链接,意思是只有当前c文件可以链接
- register:register一般修饰全局变量,作用是告诉编译器尽量考虑把变量放在寄存器中,以提高其读写效率
- extern:extern用来声明全局变量,目的是让全局变量在不同的文件之间能被引用,比如我们在a文件中定义了一个全局变量,如果b文件中要引用该全局变量,那么就必须使用extern来声明该全局变量
- 其实,函数和全局变量用法是一样的,可以不声明,直接定义使用(定义本身就有声明的功能)。但是如果要在其他文件中调用,那么就一定要声明,只不过函数的声明可以不用加extern
- volatile:volatile用来修饰变量,可以防止编译器过度优化,一般有三种情况需要使用
- 变量值(一般是寄存器)会由于硬件发生变化
- 中断isr中调用的变量值
- 多线程中公用的变量
- restrict:restrict用来修饰指针,和volatile作用相反,告诉编译器该指针指向的值只能由该指针访问,编译器可以优化该指针指向的值
4.作用域
- 作用域就是在该范围内可以看见此变量,可以访问此变量
- 局部变量的作用域是代码块作用域。代码块可以理解为是一对大括号{}括起来的部分,注意,代码块不仅仅包括函数,还包括if{},while{}等语句括起来的部分
- 函数名和全局变量的作用域是文件作用域,也就是整个.c文件
- 同名变量的优先级原则:编程时如果出现了同名变量,并且他们的作用域有交叠,那么原则就是作用域小的掩盖作用域大的,可以理解为“县官不如现管”
5.生命周期
- 栈变量(普通的局部变量和函数参数变量):生命周期是临时的,定义时被创建,函数返回时消亡
- 堆变量:内存空间的生命周期由程序员申请、释放操作决定。而指向堆内存的指针变量,其生命周期视指针变量类型而定
- 数据段、bss段变量:生命周期为永久,所谓永久不是真的永久,是伴随着应用程序的一生,随着程序诞生而诞生,消亡而消亡
- 代码段:其实就是函数(某些常量、字符串常量),生命周期也是永久的,和数据段变量相同
6.链接属性
- 外链接:普通的函数和全局变量属于外链接,意思就是可以跨文件链接。实现外链接是有前提的,在b.c中引用a.c中定义的全局变量/函数有2种方法:一是在a.h中声明该函数/全局变量,然后在b.c中
#include <a.h>
;二是在b.c中使用extern显式声明要引用的函数/全局变量。其中第一种方法比较正式 - 内连接:static修饰的函数和全局变量属于内链接,意思是只有当前c文件可以链接
- 无链接:局部变量属于无连接,意思是只能在它自己的代码块内被使用
- 由于函数和全局变量的链接属性相同,所以务必保证它们不能同名