因为讲全局变量和局部变量的时候会涉及到它们的存储位置,所以在讲全局变量和局部变量前,先科普一下进程映象的概念。
进程映象
先来区分一下程序和进程的概念:
程序:存储在磁盘上的可执行文件(二进制文件、脚本文件)
进程:正在系统中运行的程序
进程映像:系统中进程内存的分布情况
名称 | 含义 | 存储内容 | 特点 |
---|---|---|---|
text | 代码段 | 二进制指令、常量 | 权限只读,强制修改会产生段错误 |
data | 数据段 | 初始化过的全局变量、初始化过的静态局部变量 | 程序运行结束释放内存 |
bss | 静态数据段 | 未初始化过的全局变量、未初始化过的静态局部变量(初始化为零的全局变量和静态局部变量也属于bss) | 程序运行结束释放内存 |
heap | 堆 | 由程序员决定 | 由程序员手动管理(手动申请、手动释放) 优点:足够大 缺点:使用麻烦 |
stack | 栈 | 局部变量、块变量 | 由操作系统 管理,自动申请、自动释放,大小随着程序运行而变化,缺点:小,溢出会出现段错误。 |
局部变量和全局变量
1)全局变量:定义在函数外的变量
存储位置: data(初始化)/ bss(未初始化)
生命周期: 在程序运行前开始直到程序结束才释放
使用范围(作用域):程序中的任何位置
2) 局部变量:定义在函数内的变量
存储位置: stack 栈内存
生命周期: 函数调用开始到函数执行结束
使用范围(作用域): 函数内
3)块变量:块变量也属于局部变量,定义在语句块内(if \ for \ while…)
存储位置: stack 栈内存
生命周期: 定义语句开始到函数执行结束
使用范围(作用域): 只能在语句块内使用
建议:全局变量首字母大写
注意:块变量、局部变量和全局变量同名,但是局部变量会屏蔽同名的全局变量,块变量会屏蔽同名的局部变量、全局变量
类型限定符
auto
用于自动分配、释放内存的变量,局部变量不写默认auto。
auto int num; 等价于 int num;
注意:auto不能再全局变量前使用,也不能与static同时使用
extern
用于声明变量,多用于跨文件使用变量。告诉编译器此变量已经在别处定义好了,可以放心使用。
例如:当a.c中定义一个全局变量,b.c如果想要使用就需要用extern声明一下。
注意:只能临时地瞒过编译器通过编译,链接时如果找不到定义语句依然会报错,extern 不能初始化变量,否则会报错
static
static修饰的变量会改变存储位置、延长生命周期、限制作用域。如果修饰的变量未初始化,则变量的值默认为0。
1)改变存储位置
改变局部变量的存储位置,由stack 改为 data(初始化) 或者 bss(未初始化)
2)延长生命周期
延长局部变量的生命周期到程序结束,函数结束时不会被自动销毁,它们的初始化语句只执行一次。
3)限制作用域
限制全局变量、函数,只能在本文件内使用。可以防止全局变量、函数命名冲突,可以防止别人调用
const
用于 "保护"变量不被显式地修改。如果对已经初始化过的全局变量、静态局部变量用const修饰,会变成常量,存储在text区域
volatile
如果变量没有显式地修改,再次使用这个变量时不会从内存中读取,而是继续使用上一次读取结果。
变量用volatile修饰后,每次使用该变量时,不再做取值优化,每次使用变量都会从内存中读取数据。一般硬件编程、多线程编程时都要使用volatile修饰。
volatile int num = 10;
if(num == num)
{
//有可能为假
}
register
存储介质(运行速度从慢到快)
硬盘->内存->高级缓存->寄存器
用 register 修饰变量后,会申请把变量的存储介质从内存改为寄存器,但是因为寄存器数量有限,申请不一定成功。
注意:寄存器变量不能取地址 &
typedef
typedef用于类型重定义,定义变量时,如果前面加上typedef 变量名变成了类型。
例如:
typedef int num
num 等价于 int 数据类型
可以用 num 定义变量
num a; 等价于 int a;
结语
希望这篇文章对你有所帮助