QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3734|回复: 1
打印 上一主题 下一主题

[转帖]什么叫“堆”,“堆”跟“栈”有什么区别?

[复制链接]
字体大小: 正常 放大
rashige        

6

主题

3

听众

48

积分

升级  45.26%

该用户从未签到

新人进步奖

跳转到指定楼层
1#
发表于 2004-4-29 10:22 |只看该作者 |正序浏览
|招呼Ta 关注Ta
<>一、预备知识—程序的内存分配8 ~8 c7 ?! w4 C1 |2 R3 Y9 a
一个由c/C++编译的程序占用的内存分为以下几个部分6 B9 d& K! b; l% d1 w# N( n: E
1、栈区(stack)—   由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
% r# |/ @5 K' i* s! W: l2、堆区(heap) —   一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。; W. {  ^- J$ x  a+ ^
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放
( n: z# ^5 ?! L4 X- |; j4、文字常量区  —常量字符串就是放在这里的。 程序结束后由系统释放
" k* v' @" c- `/ P$ D- I; t5、程序代码区—存放函数体的二进制代码。</P>6 C7 e$ c6 p3 ]3 q7 @1 x5 t
<>二、例子程序% j$ a2 N' i: n- {
这是一个前辈写的,非常详细
  D+ Y  u/ E( {/ r' T9 D//main.cpp
4 o/ B0 X% ~$ v0 v5 e. Zint a = 0;    全局初始化区
% q$ j. P. E& d" v8 t% {4 S4 mchar *p1;     全局未初始化区 0 w5 h5 T6 r9 p6 E+ k% J' G
main() ! E$ W2 ^2 K* P1 k3 {
{ , R5 d: }/ x; v( B# k5 k8 Y! b: G
int b;              栈
5 ^0 {  A# s! h, N3 a' |$ kchar s[] = "abc";   栈
9 T" }% I$ P1 g3 B7 b$ ]+ dchar *p2;            栈
# m& V7 i; u. M% C8 M) M! Ochar *p3 = "123456";     123456\0在常量区,p3在栈上。 1 ?1 o- H, L  j! s" |( m$ `- [6 Z0 _5 ~5 ^
static int c =0;       全局(静态)初始化区
% L! Y1 @& @. a$ K2 ]5 xp1 = (char *)malloc(10); 9 H) I3 f- k; l- q
p2 = (char *)malloc(20);
4 S7 I, @( S# R0 w; t8 u分配得来得10和20字节的区域就在堆区。 ' ^  x6 B. h2 {, F6 z
strcpy(p1, "123456");  123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。
. U3 V! h7 a! A5 `}
; N6 T( W# U# j  Y</P>
9 s0 i4 n0 q! ]3 ^( E6 m<>二、堆和栈的理论知识
/ {* G% ^8 s6 S- P9 `- \7 [. R2.1申请方式
5 ^6 o. ^* o2 F% v/ {; \* \  stack:
8 g- V& D* t! B0 n* I     由系统自动分配。 例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间             ) f4 {9 h- k: r( j: r3 l' {
   heap:; e8 r* v0 y5 S* u6 X! H
     需要程序员自己申请,并指明大小,在c中malloc函数
1 y6 s; [9 u: z+ F2 R5 n       如p1 = (char *)malloc(10);
3 m! S# t2 @; `+ m- Y1 m     在C++中用new运算符
0 S$ \4 o) }8 R9 M2 r- b9 S       如p2 = (char *)malloc(10);
* `9 s1 A: i+ Z/ c9 z* r6 [5 u      但是注意p1、p2本身是在栈中的。2 Z. ?) e7 [3 f7 R5 O5 z1 W, J2 J# [/ X
</P>2 E0 z: X7 s* v) m2 B# W# x
<>2.24 ~7 ]$ P/ T* F4 h; `+ P
申请后系统的响应
$ h& E2 v( u. Z  C1 H" g   栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。) y; q8 N" N3 @* J( D* ?: y% s
   堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,
! r6 {( N( o1 t1 R- B; N" \) i+ r会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中</P>/ ]) p9 |7 G& ^6 e7 Q" H

  J3 [9 |9 Y3 @9 A4 `% C0 `7 P+ z<>2.3申请大小的限制8 Y8 A8 c+ s& s2 c- b% D
   栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。  l  h: m, O$ [; A7 J9 e# G% a
   堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。& Z! j2 S6 w  ^/ Y; H2 \
</P>" G& H4 ?! }. `0 x4 U( {
<>2.4申请效率的比较:
# T% M1 O+ k3 D$ w+ c   栈由系统自动分配,速度较快。但程序员是无法控制的。/ `# @& x6 ]0 l  h9 B1 T- V
   堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.! c1 {# f0 q: p; L, t% X
   另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是直接在进程的地址空间中保留一快内存,虽然用起来最不方便。但是速度快,也最灵活。</P>
) f3 m! x2 Z0 o" i, U0 P( R- X<>2.5堆和栈中的存储内容
" G) w) u9 K9 _4 b. D( @   栈: 在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。4 w5 A1 d9 [5 j5 ~
   当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。% |) @/ h& Y+ P! F
   堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。</P>/ d* @8 v+ Q& U

! X* s! [- v9 \" E4 m+ ]# d4 \$ N8 g<>2.6存取效率的比较</P>
4 m% ]- C7 P% Y: l/ D  x8 A. ~<>char s1[] = "aaaaaaaaaaaaaaa";
$ n; y3 a! ~% j9 x5 A) O1 mchar *s2 = "bbbbbbbbbbbbbbbbb";
. x4 s7 w" `5 B  `/ o" Kaaaaaaaaaaa是在运行时刻赋值的;
0 l" Y/ F  ~0 E而bbbbbbbbbbb是在编译时就确定的;
5 y6 H  H, v2 Y* }1 b+ O/ |2 N但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。 ' \: M7 ~3 R& G6 D7 ?( C" V
比如:
5 S6 K" y! x  E3 c0 U  J5 i. y# q#include &lt;stdio.h&gt;
  V! Z" v1 q' O" Y$ P9 D7 D8 rvoid main() 8 k* N$ A. Z5 Z; |2 ]$ \
{
9 ]- X, p+ t+ n0 |/ H0 w* Gchar a = 1;
4 X9 x1 h9 Z3 x8 M, Zchar c[] = "1234567890"; " x4 f6 d# a4 U" B2 r8 N
char *p ="1234567890";
' V! b" `5 Z& xa = c[1];
8 Y! J! o9 H9 L. N5 ?a = p[1];
) T- T, @% {& h9 n* Hreturn;
' Q" E! o$ E" l9 }" j  _. q}
: n& f, }$ b+ P; P5 J& G) `对应的汇编代码
; U2 J7 O" W# r% U& O. B) i10:   a = c[1]; 6 J) {0 E2 z, c  g
00401067 8A 4D F1 mov       cl,byte ptr [ebp-0Fh] $ {& ?" Z2 H2 I" @2 i3 _
0040106A 88 4D FC mov         byte ptr [ebp-4],cl
- Q" P+ d( X3 E7 Q11:   a = p[1]; " C1 ~# j* ~3 C! l' j; N
0040106D 8B 55 EC mov         edx,dword ptr [ebp-14h] ; b8 T- R  w. Z( F" L  C
00401070 8A 42 01 mov       al,byte ptr [edx+1] + \7 U2 H' n6 x+ Y
00401073 88 45 FC mov         byte ptr [ebp-4],al , ]* t# L  B* ?1 T9 e3 o' m
第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指针值读到edx中,在根据edx读取字符,显然慢了</P>
, u$ f# Q7 [9 t% r$ Y: ^' X- f* \/ _7 H
<>2.7小结:
' Q, d5 y6 i# w5 \: R( F" {   堆和栈的区别可以用如下的比喻来看出:
) F, K; S" j+ Q6 G3 c6 B- ?4 X6 f0 \   使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。
0 f& \7 ^: u" v; }    使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。</P>
zan
转播转播0 分享淘帖0 分享分享0 收藏收藏0 支持支持0 反对反对0 微信微信
ilikenba 实名认证       

1万

主题

49

听众

2万

积分

  • TA的每日心情
    奋斗
    2024-6-23 05:14
  • 签到天数: 1043 天

    [LV.10]以坛为家III

    社区QQ达人 新人进步奖 优秀斑竹奖 发帖功臣

    群组: 万里江山

    群组: sas讨论小组

    群组: 长盛证券理财有限公司

    群组: C 语言讨论组

    群组: Matlab讨论组

    <>学习,</P><>其实简单的说直接用类型名申请的变量都是放在了栈中,但是有时这并不能满足您的需要,可以用malloc或new在堆中申请空间,但要注意的是栈中的空间系统会自动收回,而对中的则需要用delete回收!</P>
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 注册地址

    qq
    收缩
    • 电话咨询

    • 04714969085
    fastpost

    关于我们| 联系我们| 诚征英才| 对外合作| 产品服务| QQ

    手机版|Archiver| |繁體中文 手机客户端  

    蒙公网安备 15010502000194号

    Powered by Discuz! X2.5   © 2001-2013 数学建模网-数学中国 ( 蒙ICP备14002410号-3 蒙BBS备-0002号 )     论坛法律顾问:王兆丰

    GMT+8, 2026-10-26 08:05 , Processed in 1.299879 second(s), 58 queries .

    回顶部