堆和栈的主要区别

本文详细解析了堆和栈的申请方式、回收方式、申请后的系统响应、申请大小限制、申请效率、存储内容及存取效率等内容,并通过具体示例对比了两者的区别。
1申请方式和回收方式    
  stack:   由系统自动分配。  
例如,声明在函数中一个局部变量 int b;  
系统自动在栈中为b开辟空间    
  heap:    需要程序员自己申请,并指明大小,在c中malloc函数    
  如p1   =   (char   *)malloc(10);    
  在C++中用new运算符    
  如p2   =   new   char[10];    (注意p1、p2本身是在栈中的)    

由于栈上的空间是自动分配自动回收的,所以栈上的数据的生存周期只是在函数的运行过程中,运行后就释放掉,不可以再访问。而堆上的数据只要程序员不释放空间,就一直可以访问到,不过缺点是一旦忘记释放会造成内存泄露
   
2申请后系统的响应    
  栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。    
  堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。  
  另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。
也就是说堆会在申请后还要做一些后续的工作这就会引出申请效率的问题。    

3申请大小的限制    
  栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。    
  堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。     
   
4申请效率的比较   
  栈由系统自动分配,速度较快。但程序员是无法控制的。    
  堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.    
  (另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是直接在进程的地址空间中保留一块内存,虽然用起来最不方便。但是速度快,也最灵活)  
 
5堆和栈中的存储内容    
  栈:在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。    
  当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。    
  堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容由程序员安排。    
   
6存取效率的比较      
  char   s1[]   =   "aaaaaaaaaaaaaaa";    
  char   *s2   =   "bbbbbbbbbbbbbbbbb";    
  aaaaaaaaaaa是在运行时刻赋值的;    
  而bbbbbbbbbbb是在编译时就确定的;    
  但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。    
  比如:    
  #include    
  void   main()    
  {    
  char   a   =   1;    
  char   c[]   =   "1234567890";    
  char   *p   ="1234567890";    
  a   =   c[1];    
  a   =   p[1];    
  return;    
  }    
  对应的汇编代码    
  10:   a   =   c[1];    
  00401067   8A   4D   F1   mov   cl,byte   ptr   [ebp-0Fh]    
  0040106A   88   4D   FC   mov   byte   ptr   [ebp-4],cl    
  11:   a   =   p[1];    
  0040106D   8B   55   EC   mov   edx,dword   ptr   [ebp-14h]    
  00401070   8A   42   01   mov   al,byte   ptr   [edx+1]    
  00401073   88   45   FC   mov   byte   ptr   [ebp-4],al    
  第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指针值读到  
  edx中,再根据edx读取字符,显然慢了。

小结:    
  堆和栈的区别可以用如下的比喻来看出:    
  使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。    
  使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。
<think>我们正在讨论数据结构中的队列。用户已经提供了几个引用,我们需要从中提取有用的信息,并结合我们的知识来回答用户的问题。 用户的问题有两个部分: 1. 区别 2. 队列的区别 根据引用,我们可以总结如下: 关于(Heap): - 是一种动态分配内存的区域,大小不固定,可以随时扩展或收缩(引用[1])。 - 适合存储动态大小的数据,如动态数组、链表、树等(引用[1])。 - 的内存管理需要程序员手动进行(如使用malloc, free等)(引用[1])。 关于(Stack): - 是一种线性数据结构,遵循后进先出(LIFO)原则(引用[2][3])。 - 中的数据占用的内存空间大小是确定的,由系统自动分配释放(引用[2])。 - 的操作主要有PUSH(入POP(出)(引用[3])。 关于队列(Queue): - 队列是一种线性数据结构,遵循先进先出(FIFO)原则(引用[1][3])。 - 队列的操作包括入队(enqueue)出队(dequeue)。 现在,我们分别回答两个问题: 一、区别: 1. 数据结构性质: - :通常不是指数据结构中的(注意:数据结构中的通常指二叉,是一种完全二叉树,分为最大最小),但这里用户引用的内容主要讨论内存管理中的。在内存管理中,是动态分配的内存区域。 - :是一种具有LIFO特性的数据结构。 2. 内存管理角度(在程序运行内存中的): - 分配方式: :动态分配,由程序员控制(手动申请释放)。 :由系统自动分配释放,如函数调用时局部变量的存储。 - 大小: :大小不固定,可动态扩展。 :大小固定,通常较小。 - 效率: :分配释放需要手动管理,可能产生内存碎片,效率相对较低。 :系统自动管理,效率高。 - 数据存储内容: :存储动态分配的数据,如malloc分配的内存。 :存储函数调用信息(如返回地址、参数)、局部变量等。 3. 数据结构角度(如果指数据结构中的): - (二叉):是一种特殊的树形结构,通常用于实现优先队列。 - :线性结构,后进先出。 注意:用户的问题中引用的内容混合了内存管理数据结构两个概念。在回答时,我们需要澄清这两个不同的语境。 二、队列的区别: 1. 数据操作原则: - :后进先出(LIFO),即最后压入的元素最先弹出。 - 队列:先进先出(FIFO),即最先进入队列的元素最先出队。 2. 操作名称: - :插入操作称为压(PUSH),删除操作称为弹(POP)。 - 队列:插入操作称为入队(enqueue),删除操作称为出队(dequeue)。 3. 应用场景: - :函数调用、表达式求值、括号匹配、递归等。 - 队列:任务调度、缓冲处理、广度优先搜索等。 根据以上分析,我们组织回答如下:</think>### 与队列的区别分析 #### 一、区别(内存管理角度) | 特性 | (Heap) | (Stack) | |--------------|-------------------------------|--------------------------------| | **管理方式** | 手动分配释放 (`malloc`/`free`)| 系统自动管理 | | **内存大小** | 动态扩展/收缩 | 固定大小(编译时确定)[^2] | | **访问速度** | 较慢(需寻址) | 极快(直接操作指针) | | **存储内容** | 动态对象、全局变量 | 局部变量、函数参数[^1] | | **碎片问题** | 易产生内存碎片 | 无碎片问题 | | **生命周期** | 显式释放前持续存在 | 函数结束自动释放[^2] | > **关键区别**:像"自选仓库"(需手动管理),像"自动传送带"(系统托管)[^1] #### 二、与队列的区别(数据结构角度) | 特性 | (Stack) | 队列 (Queue) | |--------------|-------------------------------|-------------------------------| | **操作原则** | 后进先出 (LIFO)[^3] | 先进先出 (FIFO)[^1] | | **核心操作** | `push`(入)/`pop`(出) | `enqueue`(入队)/`dequeue`(出队)| | **存储结构** | 单端操作(仅顶) | 双端操作(队头出/队尾入)[^4]| | **内存管理** | 系统自动分配释放[^2] | 通常需手动实现内存管理 | | **典型应用** | 函数调用、括号匹配 | 任务调度、打印队列[^1] | ```mermaid graph LR A[数据结构] --> B[ LIFO] A --> C[队列 FIFO] B --> D[操作:push/pop] C --> E[操作:enqueue/dequeue] D --> F[应用:递归调用] E --> G[应用:消息缓冲] ``` #### 三、典型应用场景 1. **的应用** - 动态内存分配(如`malloc`) - 复杂数据结构存储(树、图) - 大小不确定的数据缓冲区[^1] 2. **的应用** - 函数调用(保存返回地址/局部变量) - 表达式求值($3 \times (4 + 2)$) - 回溯算法(迷宫求解)[^3] 3. **队列的应用** - 消息队列(生产者-消费者模型) - 广度优先搜索(BFS) - 多线程任务调度[^4] > **内存布局示例**(C程序运行时): > ``` > 高地址 > ┌────────────┐ > │ │ ← 动态增长 > ├────────────┤ > │ │ ← 动态增长 > ├────────────┤ > │ 全局变量 │ > ├────────────┤ > │ 程序代码 │ > └────────────┘ 低地址 > ``` #### 四、技术本质总结 1. **** - 动态内存池,需手动管理 - 分配策略:首次适应/最佳适应算法 - 内存碎片问题需关注 2. **** - 函数执行的基础框架 - 通过指针寄存器(如x86的`ESP`)高效访问 - 溢出是常见安全漏洞 3. **队列** - 实现方式多样(数组/链表/循环队列) - 循环队列解决"假溢出":`front = (front+1) % size`[^4] - 优先队列=+队列(按优先级出队) --- ### 相关问题 1. 为什么内存分配比快? 2. 如何用两个实现一个队列? 3. 循环队列如何判断队满/队空?[^4] 4. 内存泄漏的检测方法有哪些? 5. 函数调用的具体工作原理是什么?[^3]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值