比较全局变量、全局静态变量、局部变量、局部静态变量的区别,他们在编译完后存储位置在什么地方、初始化值在什么地方、内存什么时候分配、赋初值对这些变量有哪些影响等。要弄清楚这些问题,首先要弄清楚下面几个知识点。
C语言分下面几个存储区:
1、栈区(stack) 由编译器在需要的时候自动分配释放,在不需要的时候就自动清除的变量存储区。通常存放的变量是函数的参数值、局部变量的值等,其操作方式类似于数据结构中的栈。
2、堆区(heap) 一般由程序员去分配释放,和编译器完全没有关系,直接由我们的应用程序去控制,一般分配一块内存就对应一个回收一块内存。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。
3、全局(静态存储区) 全局变量和静态变量的存储是放在一块内存中的,全局变量又分为初始化的和未初始化的。初始化的全局变量和静态变量存储在一块区域内,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序运行结束后由系统释放。
4、常量存储区这是一块比较特殊的存储区,他存放的是常量,不允许修改的常量。
5、程序代码段存放函数体的二进制代码。
内存中存放数据的数据段有以下几种:
1、bss 是英文block started by symbol的简称,通常是指用来存放程序中未初始化的全局变量的一块内存区域,在程序载入时由内核清0。bss段属于静态内存分配。它的初始值也是由用户自己定义的链接定位文件所确定,用户应该将它定义在可读写的ram区内,源程序中使用malloc分配的内存就是这一块,它不是根据data大小确定,主要由程序中同时分配内存最大值所确定,不过如果超出了范围,也就是分配失败,可以等空间释放之后再分配。
2、text 段是程序代码段,在at91库中是表示程序段的大小,它是由编译器在编译连接时自动计算的,当你在链接定位文件中将该符号放置在代码段后,那么该符号表示的值就是代码段大小,编译连接时,该符号所代表的值会自动代入到源程序中。
3、data 包含静态初始化的数据,所以有初值的全局变量和static变量在data区。段的起始位置也是由连接定位文件所确定,大小在编译连接时自动分配,它和你的程序大小没有关系,但和程序使用到的全局变量,常量数量相关。
4、stack 保存函数的局部变量和参数。是一种“后进先出”(last in first out,lifo)的数据结构,这意味着最后放到栈上的数据,将会是第一个从栈上移走的数据。对于哪些暂时存储的信息,和不需要长时间保存的信息来说,lifo这种数据结构非常理想。在调用函数或过程后,系统通常会清除栈上保存的局部变量、函数调用信息及其它的信息。栈另外一个重要的特征是,它的地址空间“向下减少”,即当栈上保存的数据越多,栈的地址就越低。栈(stack)的顶部在可读写的ram区的最后。
5、heap 保存函数内部动态分配内存,是另外一种用来保存程序信息的数据结构,更准确的说是保存程序的动态变量。堆是“先进先出”(first in first out,fifo)数据结构。它只允许在堆的一端插入数据,在另一端移走数据。堆的地址空间“向上增加”,即当堆上保存的数据越多,堆的地址就越高。
变量可以分为全局变量、静态全局变量、静态局部变量和局部变量。
按照存储区分:全局变量、静态全局变量和静态局部变量都存放在内存的全局数据区,局部变量存放在内存的栈区。
按作用域分:全局变量在整个工程文件内都有效;静态全局变量只在定义它的文件内有效;静态局部变量只在定义它的函数内有效,只是程序仅分配一次内存,函数返回后,该变量不会消失;局部变量在定义它的函数内有效,但是函数返回后失效。
全局变量和静态变量如果没有手工初始化,则由编译器初始化为0.局部变量的值是不可知的。静态变量和全部变量完全是两码事。
静态变量是相对于自动变量,之所以称为静态变量,是说静态变量不是随着程序作用域的改变而销毁,但是静态变量的访问受到作用域的制约。自动变量是在函数调用栈中分配的,因此随着函数的退出,自动变量不能继续保存原来的值。而静态变量是在堆中分配的,编译器会对静态变量进行初始化,并且静态变量在整个程序中只有一个,而不像自动变量那样,会根据函数调用的不同具有多个副本。静态变量的值在函数退出后不变。
全局变量是指变量的作用域范围是全局可见的,而变量作用域决定于变量生命的位置。就是说在所有花括号之外声明的变量既是全局变量。
各个变量的比较区别。
从作用域看:
全局变量和局部变量:全局变量和局部变量的区别主要在于身存周期不同,全局变量在整个程序生成期可见,局部变量在自己的作用域可见。全局变量的内存分配是静态的,如果没有赋初值,会被初始化为0。局部变量的内存分配是动态的,位于堆栈中,如果没有初始化,初值视当前内存内的值而定。
全局变量具有全局作用域。全局变量只需在一个源文件中定义,就可以作用于所有的源文件。当然,其他不包括全局变量定义的源文件需要用extern关键字再次声明这个全局变量。
静态全局变量也具有全局作用域,他与全局变量的区别在于如果程序包含多个文件的话,他作用于定义它的文件里,不能作用到其他文件里,即被static关键字修饰过的变量具有文件作用域。这样即使两个不同的源文件都定义了相同的静态全局变量,他们也是不同的变量。
局部变量也只有局部作用域,他是自动对象,他在程序运行期间不是一直存在,而是只在函数执行期间存在,函数的一次调用结束后,变量就被撤销,其所占用的内存也被收回。
静态局部变量具有局部作用域。它只被初始化一次,自从第一次初始化直到程序结束都一直存在,他和全局变量的区别在于全局变量对所有的函数都是可见的,而静态局部变量只对定义自己的函数体始终可见。
int fun(int i) { static int a = i; return a; } for (int i = 1; i < 10; ++i) { cout << fun(i) << endl;//输出结果总是为1 }
从内存分配看:
全局变量、静态局部变量、静态全局变量都在静态存储区分配空间,而局部变量在栈分配空间。
全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式。这两者在存储方式上没有什么不同。区别在于非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。而静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效,在同一源程序的其他源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其他源文件中引起错误。
1、静态变量会被放在程序的静态数据存储区里,这样可以在下一次调用的时候还可以保持原来的赋值。这一点是他与堆栈变量和堆变量的区别
2、变量用static告知编译器,自己仅仅在变量的作用域范围内可见。这一点是他与全局变量的区别。
从以上分析可以看出,把局部变量改变为静态变量后是改变了他的存储方式,即改变了他的生存期。把全局变量改变为静态变量后是改变了他的作用域,限制了他的使用范围,因此static这个说明符在不同的地方起的作用是不同的。
不同类型的变量在内存中的位置:
1、 已经初始化的全局变量存放与data数据段;未初始化的全局变量存放与bss数据段。
2、 静态的全局变量存放与data数据段
3、 局部变量存放在栈上。
4、 静态局部变量,并不是在调用函数时分配函数返回时释放,而是像全局变量一样静态分配,存放data数据段,但它的作用域在函数中起作用。