在认识内存分布之前,先认识全局变量、局部变量、静态变量
从作用域上分:
1>全局变量具有全局作用域。全局变量只需在一个源文件中定义,就可以作用于所有的源文件。当然,其他不包含全局变量定义的源文件需要用extern关键字再次声明这个全局变量。
2>静态局部变量具有局部作用域,它只被初始化一次,自从第一次被初始化直到程序运行结束都一直存在,它和全局变量的区别在于全局变量对所有的函数都是可见的,而静态局部变量只对定义自己的函数体始终可见。
3>局部变量也只有局部作用域,它是自动对象(auto),它在程序运行期间不是一直存在,而是只在函数执行期间存在,函数的一次调用执行结束后,变量被撤销,其所占用的内存也被收回。
4>静态全局变量也具有全局作用域,它与全局变量的区别在于如果程序包含多个文件的话,它作用于定义它的文件里,不能作用到其它文件里,即被static关键字修饰过的变量具有文件作用域。这样即使两个不同的源文件都定义了相同名字的静态全局变量,它们也是不同的变量。
总结:
变量类型前有没有加static这个关键词和变量在函数体内外声明,决定了变量的作用域。
在分配内存空间上看:
1>全局变量,静态局部变量,静态全局变量都在静态存储区分配空间,而局部变量在栈里分配空间
2>全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式。这两者在存储方式上并无不同。这两者的区别在于非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。而静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效,在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。
(1)全局变量、静态全局变量以及静态局部变量都会被放在程序的静态数据存储区(全局可见)中,这样可以在下一次调用的时候还可以保持原来的赋值。这一点是它们与堆变量的区别。
(2)静态变量(包括静态局部变量和静态全局变量)用static告知编译器,自己仅仅在变量的作用范围内可见。这一点是它与全局变量的区别。
总结:局部变量变为静态局部变量改变了局部变量的生存周期和存储方式,全局变量改为静态全局变量改变了它的作用域。
一个可运行程序在存储与在运行过程中,内存划分的方式是不一样的。
程序存储的时候,内存分为代码段(.text),未初始化变量区域(bss),初始化变量区域(data segment)
(1)代码区(text segment)。代码区指令根据程序设计流程依次执行,对于顺序指令,则只会执行一次(每个进程),如果反复,则需要使用跳转指令,如果进行递归,则需要借助栈来实现。
代码区的指令中包括操作码和要操作的对象(或对象地址引用)。如果是立即数(即具体的数值,如5),将直接包含在代码中;如果是局部数据,将在栈区分配空间,然后引用该数据地址;如果是BSS区和数据区,在代码中同样将引用该数据地址。
(2)全局初始化数据区/静态数据区(Data Segment)。只初始化一次。
(3)未初始化数据区(BSS)。在运行时改变其值。
(4)栈区(stack)。由编译器自动分配释放,存放函数的参数值、局部变量的值等。其操作方式类似于数据结构中的栈。每当一个函数被调用,该函数返回地址和一些关于调用的信息,比如某些寄存器的内容,被存储到栈区。然后这个被调用的函数再为它的自动变量和临时变量在栈区上分配空间,这就是C实现函数递归调用的方法。每执行一次递归函数调用,一个新的栈框架就会被使用,这样这个新实例栈里的变量就不会和该函数的另一个实例栈里面的变量混淆。空间大小由编译器开辟,但是不够灵活。
(5)堆区(heap)。用于动态内存分配。堆在内存中位于bss区和栈区之间。一般由程序员分配和释放,若程序员不释放,程序结束时有可能由OS回收。可能造成内存泄漏
之所以分成这么多个区域,主要基于以下考虑:
一个进程在运行过程中,代码是根据流程依次执行的,只需要访问一次,当然跳转和递归有可能使代码执行多次,而数据一般都需要访问多次,因此单独开辟空间以方便访问和节约空间。
临时数据及需要再次使用的代码在运行时放入栈区中,生命周期短。
全局数据和静态数据有可能在整个程序执行过程中都需要访问,因此单独存储管理。
堆区由用户自由分配,以便管理。