全局变量和静态变量以及内存堆栈的关系

在编写程序时,内存的控制是很重要的一部分。关于全局变量和局部变量,静态变量的关系如何?以及他们在内存在是存储于哪部分的?做个记录,以便往后查看。

1. 全局变量和静态变量

全局变量:又称外部变量,与之相对的是局部变量,是从变量的作用域上来考量的。全局变量定义与函数外面,作用于整个程序;而局部变量则是定义在函数里面,仅作用于函数内部,无法跨函数作用。全局变量为静态分配,即程序执行之前就进行内存分配,不再改变(内存不再改变);而局部变量则是动态分配,在程序执行的时候进行内存分配。

静态变量:是从变量的内存分配上来考量的,与之相对的是动态变量。静态变量如其名,采用静态分配方式的变量,表现在代码程层面就是用 static 或 extern 关键字修饰的变量即为静态变量。静态变量分为static和extern,两者作用域不同,static修饰的只能作用于单个程序文件,而extern 则可以作用于整个程序(多文件)。

 全局变量和静态全局变量:当程序只有一个文件构成时,两个没有什么区别;当多个文件存在时,static全局变量则使得该变量为源文件独享(只作用于单个文件),而 extern 全局变量则可以作用整个程序,包括其他源文件。

那么静态局部变量呢?静态局部变量作用域与局部变量一致,只不过生命周期不一样,内存存储地点也不一样。静态局部变量是程序执行之前静态分配内存,程序执行结束之后结束。

看到有人提到静态变量无法改变,这是错误的。静态变量只是内存分配为静态分配方式,内存不变,并不是说不可以改变,静态变量也是可以更改存储内容的。

2. 全局变量,静态变量在内存中存放位置

我们知道一个汇编程序分为:代码段,数据段,堆栈段(这里的堆栈就是指的栈,stack)。从内存上说,数据段又可分为:静态存储区,堆(heap)和BSS(Block Started by Symbol)区。

代码段:就是存放程序二进制代码区域

静态存储区(.data):用于存放已初始化的全局变量和静态变量

堆(heap):数据区,用于动态分配的存储。一般由程序员控制分配和释放,malloc 和 new 的数据存放于堆中

未初始化全局(静态)变量BSS:未初始化的全局变量和静态变量,全局变量和静态变量默认初始化为0

堆栈(stack):通常理解就是局部变量,函数调用等所需存储

为了了解全局变量和静态变量等内存存储位置,talk is cheap, show the code! 给出一个测试案例,然后用objdump 或者 readelf反汇编指令,进行查看该程序生成的对应汇编程序结构,获取各个变量的内存段分配。

 
  1. // tets.c

  2. #include <stdio.h>

  3.  
  4. static int var_global_static_a = 1; // 初始化静态全局变量

  5. static int var_global_static_b; // 未初始化静态全局变量

  6.  
  7. int var_global_a = 1; // 初始化全局变量

  8. int var_global_b; // 未初始化全局变量

  9.  
  10. int main()

  11. {

  12. static int var_static_a = 1; // 初始化静态局部变量

  13. static int var_static_b; // 未初始化静态局部变量

  14.  
  15. int var_a = 1; // 初始化局部变量

  16. int var_b; // 未初始化局部变量

  17. return 0;

  18. }

结果如下图所示:

objdump -t test.o

 执行结果可知:已初始化的全局变量和静态变量(无论全局还是局部)都是存放于.data 段,而未初始化的静态变量则是存放在.bss 段,未初始化的全局变量存放在 .comment,这个不知道是干嘛的,但其实也是应该是 .bss 类似。而局部变量则并未出现在这里,因为局部变量位于堆栈段,是需要程序执行时进行分配的,所以这个只进行了编译的obj文件中并没有这两个变量的内存分配。

.datavar_global_static_a初始化全局变量和静态变量
var_static_a
var_global_a
.bssvar_global_static_b未初始化全局变量和静态变量
var_static_b
var_global_b (显示是 .COM)
未分配var_a局部变量
var_b

如果全局变量初始化都为 0 呢?结果如下所示,全部都变成了 .bss 段。其实0是全局变量和静态变量的默认初始化值,即如果没有主动初始化而直接使用,则是默认初始化为0 。所以当主动初始化为0时,还是当成没有初始化处理,存放于 .bss 段

3.  堆(heap)和堆栈段(stack)

 堆和栈总是一起别人提起,所以这里也放在一起来讲。有个概念需要区分开的,就是堆栈指的就是内存的stack部分,而不是堆和栈,堆是heap。有时候定义数组,因为过大,直接定义成局部变量的话就会爆,但是用 malloc 或者将数组变为全局变量,就不会爆。我们知道这这分别对应着堆栈存储 和 数据段(堆 和 静态存储区),那么他们的大小分别是多少呢?

3.1 堆的大小和堆溢出

堆作为数据段的一部分,理论上除去堆栈大小,代码段大小和全局变量静态变量等部分,剩下的都可以作为堆的内存分配范围。所以堆的大小一般比较大,几乎接近计算机内存(+虚拟内存)大小。关于堆溢出,如果持续进行 malloc 而不释放的话,那么就会产生堆溢出,爆堆。

3.2 堆栈大小和栈溢出

关于堆栈的大小,Linux下有个指令可以查看,一般情况下为8-10M。当局部变量大小大于这个数值或者递归层数过多时,进行发生栈溢出。

 
  1. ulimit -a

  2.  
  3. # ulimit -s //可直接查看堆栈大小

 这里显示栈大小8M,可以用程序测试一下,栈的大小到底是否是这么大。测试程序参考 linux下栈空间大小(ulimit)

 
  1. #include <stdio.h>

  2. #include <stdlib.h>

  3.  
  4. void test(int i)

  5. {

  6. char temp[1024*1024] = {0};

  7. printf("%s %d num = %d!\r\n", __FUNCTION__, __LINE__, ++i);

  8. test(i);

  9. }

  10.  
  11. int main(void)

  12. {

  13. test(0);

  14. return 0;

  15. }

测试结果确并未如我上面参考的那个博主的一样,他得出Linux下主线程栈大小比子线程多大概2M的样子,可是我的测试结果都是一样(至少在M级是一样),结果如下:

结果证实了此前查询得出的栈大小为8M 这个事实(因为还有参数传递,和局部变量,所以是肯定到达不了8的)。为了知道我计算机的实际栈大小,以及后续的证实,进行了手动二分来确定栈具体大小(有点傻。。)

理论上堆栈空间8M=8*1024*1024 = 2*1024*1024 = 2097152 个int

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值