C语言堆栈与静态区的理解

简介

什么是代码区、常量区、全局/静态区、堆区、栈区? 每次听到这些区头都大了,很混乱而且经常忘记。其实C语言程序常涉及的基本就5个内存区:

  • 栈区主要用于函数调用的使用
  • 堆区主要是用于内存的动态申请和归还
  • 全局/静态存储区用于保存全局变量和静态变量
  • 常量区存放的是常量,不允许修改,程序结束后由系统释放。
  • 代码区用于存放函数体的二进制代码,也是由系统管理。

下面来重点温习一下栈,堆和静态区:

栈区

栈是现代计算机程序里最为重要的概念之一。
栈在程序中用于维护函数调用上下文,没有栈就没有函数,没有局部变量。栈保存了一个函数调用所需的维护信息:

  • 函数参数,函数返回地址
  • 局部变量
  • 函数调用上下文

栈上的数据在函数调用时申请在函数返回后就会被释放掉,无法传递到函数外部,如:局部数组,局部变量等。

栈内存是由编译器自动分配与释放的,它有两种分配方式:静态分配和动态分配。

  • 静态分配是由编译器自动完成的,如局部变量的分配(即在一个函数中声明一个int类型的变量i时,编译器就会自动开辟一块内存以存放变量i)。与此同时,其生存周期也只在函数的运行过程中,在运行后就释放,并不可以再次访问。
  • 动态分配由 alloca 函数进行分配,但是栈的动态分配与堆是不同的,它的动态分配是由编译器进行释放,无需任何手工实现。值得注意的是,虽然用 alloca 函数可以实现栈内存的动态分配,但 alloca 函数的可移植性很差,而且在没有传统堆栈的机器上很难实现。因此,不宜使用于广泛移植的程序中。当然,完全可以使用C99中的变长数组来替代 alloca 函数(在C99标准之前,C语言是不支持变长数组的,如果想要动态开辟栈内存以达到变长数组的功能就得依靠 alloca 函数)。
  • 另外栈内存由一个栈指针来开辟和回收,栈内存是从高地址向低地址增长的,增长时,栈指针向低地址方向移动,指针的地址值也就相应的减小;回收时,栈指针向高地址方向移动,地址值也就增加。所以栈内存的开辟和回收都只是指针的加减,由此相对于分配堆内存可以获得一定的性能提升。

堆区

堆是程序中一块巨大的内存空间,堆内完全是由程序员手动申请与释放的,程序在运行的时候由程序员使用内存分配函数(如 malloc 函数)来申请任意多少的内存,使用完再由程序员自己负责使用内存释放函数(如 free 函数)释放内存,堆中被程序申请使用的内存在程序主动释放前将一直有效。

但是对堆来说,频繁分配和释放(malloc/free)不同大小的堆空间势必会造成内存空间的不连续,从而造成大量碎片,导致程序效率降低;而对栈来讲,则不会存在这个问题。

静态区

又叫全局区,分为全局初始化区(data segment)和未初始化区(bss segment)。全局的未初始化变量存在于.bss段中,具体体现为一个占位符;全局的已初始化变量存于.data段中。.bss是不占用可执行文件空间的(并不给该段的数据分配空间,只是记录数据所需空间的大小),其内容由操作系统初始化(清零);而.data却需要占用,其内容由程序初始化。

静态区是由编译器自动分配和释放的,即内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在,直到整个程序运行结束时才被释放,如全局变量与static变量。
与栈和堆不同,静态存储区的信息最终会保存到可执行程序中。

效率

大家都知道,栈是机器系统提供的数据结构,计算机会在底层对栈提供支持,例如,分配专门的寄存器存放栈的地址,压栈出栈都有专门的执行指令,这就决定了栈的效率比较高。一般而言,只要栈的剩余空间大于所申请空间,系统就将为程序提供内存,否则将报异常提示栈溢出。

而堆则不同,它是由 C/C++ 函数库提供的,它的机制也相当复杂。例如,为了分配一块堆内存,首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆节点,然后将该节点从空闲节点链表中删除,并将该节点的空间分配给程序。而对于大多数系统,会在这块内存空间的首地址处记录本次分配的大小,这样,代码中的 delete 语句才能正确释放本内存空间。另外,由于找到的堆节点的大小不一定正好等于申请的大小,系统会自动将多余的那部分重新放入空闲链表中。很显然,堆的分配效率比栈要低得多。

区的大小

由于操作系统是用链表来存储空闲内存地址(内存区域不连续)的,同时链表的遍历方向是由低地址向高地址进行的。因此,堆内存的申请大小受限于计算机系统中有效的虚拟内存。

而栈则不同,它是一块连续的内存区域,其地址的增长方向是向下进行的,向内存地址减小的方向增长。由此可见,栈顶的地址和栈的最大容量一般都是由系统预先规定好的,如果申请的空间超过栈的剩余空间时,将会提示溢出错误。由此可见,相对于堆,能够从栈中获得的空间相对较小。

  • 7
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值