| 设为主页 | 保存桌面 | 手机版 | 二维码 | 中国智能化网
59

绵阳维博电子有限责任公司

研发生产WB系列电量隔离传感器/智能电量变送器

智能化网头条分类
联系方式
  • 联系人:徐莎
  • 电话:0816-2278150
  • 邮件:wb@wbdz.cn
  • 传真:0816-2270571
友情链接
您当前的位置:首页 » 智能化网头条 » 维博电子:堆和栈的区别及其应用
智能化网头条
维博电子:堆和栈的区别及其应用
发布时间:2019-07-09        浏览次数:866        返回列表

张松

绵阳市维博电子有限责任公司

【摘 要】:简述了程序在内存中的映射,详细介绍了堆和栈的内存分配方式及其特征,并结合论理与实际来说明应用中出现问题的原因,分析堆和栈的优势与劣势,为在实际编程中堆和栈的使用提供依据。

【关键词】:堆;栈;应用。

一、程序在内存中的映射

一般说来,默认的动态存储环境通过堆栈机制建立。所有局部变量及所有按值传递的函数参数都通过堆栈机制自动分配内存空间,分配同一数据类型相邻块的内存区域被称为缓冲区。图1展示了程序在内存中的映射。

图1 程序在内存中的映射图

代码段(.text),存放着程序的机器码和只读数据,可执行指令就是从这里取得的。如果可能,系统会安排好相同程序的多个运行实体共享这些实例代码。这个段在内存中一般被标记为只读,以防止程序由于意外而修改其指令,任何对该区的写操作都会导致段错误(Segmentation Fault)。

数据段,在编译时分配,它包括初始化数据段(.data)和未初始化数据段(.bss)。

1)初始化数据段。通常将此称为数据段,用来存放保存全局的和静态的已初始化变量。例如,C程序中任何函数之外的声明:

int maxcount = 99;

2)非初始化数据段。通常将此段称为bss段,这一名称来源于早期汇编程序一个操作符,意思是“由符号开始的块”(block started by symbol),用来保存全局的和静态的未初始化变量,在程序开始执行之前,内核将此段中的数据初始化为0或空指针。如函数外的声明:

long sum[2000];

堆栈段,分为堆(Heap)和栈(Stack)。堆用来存储程序运行时分配的变量;而栈则是一种用来存储函数调用时的临时信息的结构,如函数调用所传递的参数、函数的返回地址、函数的局部变量等。在程序运行时由编译器在需要的时候分配,在不需要的时候自动清除。

图2 程序段

二、堆和栈的区别

堆(Heap)和栈(Stack)是有区别的,很多程序员混淆堆栈的概念,或者认为它们就是一个概念。

栈。自动变量以及每次函数调用时所需保存的信息都存放在此段中。每次函数调用时,其返回地址以及调用者的环境信息(如某些机器寄存器的值)都存放在栈中。然后,近被调用的函数在栈上为其自动和临时变量分配存储空间。通过以这种方式使用栈,C递归函数可以工作。递归函数每次调用自身时,就用一个新的栈帧,因此次函数调用实例中的变量集不会影响另一次函数调用实例中的变量。

堆。通常在堆中进行动态存储分配。由于历史上形成的惯例,堆位于未初始化数据段和栈之间。

堆和栈之间的主要区别可以表现在如下三个方面。

(1)分配和管理方式不同

堆是动态分配的,其空间的分配和释放都由程序员控制。也就是说,堆的大小并不固定,可动态扩张或缩减,其分配由malloc()等这类实时内存分配函数来实现。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)。而栈由编译器自动管理,其分配方式有两种:静态分配和动态分配。静态分配由编译器完成,比如局部变量的分配配。动态分配由alloca()函数进行分配,但是栈的动态分配和堆是不同的,它的动态分配是由编译器进行释放,无需手工控制。

(2)产生碎片不同

对堆来说,频繁执行malloc或free势必会造成内存空间的不连续,形成大量的碎片,使程序效率降低;而对栈而言,则不存在碎片问题。

(3)内存地址增长的方向不同

堆是向着内存地址增加的方向增长的,从内存的低地址向高地址方向增长;而栈的增长方向与之相反,是向着内存地址减小的方向增长的,由内存的高地址向低地址方向增长。

三、堆和栈在编程中的应用

1、 段错误

图3 程序段

以上程序段中,thread_fun为线程函数,在该函数中定义了一个ST_DATA_INF结构体的数组,结构体ST_DATA_INF的大小为4M,所以这个数组的大小为16M。即在栈中申请一个16M的空间,如果线程栈大小没有设置时,默认为8M,运行时会出现段错误(Segmentation Fault)。

虽然设置线程栈大小,可以解决该问题避免段错误,但对于这种占用大量栈空间的变量,特别是当有多个线程实例时,建议使用malloc在堆中动态申请。特别是对于那种不同线程实例中,数组长度不确定的时候,以节省内存的使用。

图4 程序段

2、 函数入口参数

图1中程序段函数int fun(ST_DATA_INF st_data_inf),入口参数同样是在栈中申请空间,可以使用指针作为入口参数,减少栈空间的使用。

3、 空间与效率

栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出(overflow)。因此,能从栈获得的空间较小。

堆:系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找个空间大于所申请空间的堆节点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。堆是向高地址扩展的数据结构,是不连续的内存区域,堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。

有寄存器直接对栈进行方位(esp,ebp),而对堆访问,只能是间接寻址。也就是说,可以直接从地址取数据放至目的地址;使用堆时,步将分配的地址放到寄存器,然后取出这个地址的值,然后放到目标地址。栈中数据CPU命中率更高,满足局部性原理。栈是编译时系统自动分配空间,而堆是动态分配(运行时分配空间),所以栈的速度快。另外,栈是先进先出的队列结构,比堆结构相对简单,分配速度大于堆。

综上所述,栈的空间有限,堆的空间灵活,但栈的访问速度快。所以对于堆和栈的使用,可以根据程序中数据所需内存大小及访问速度来确定该使用哪种方式。

**本页面信息涉及广告内容**