QQ登录

只需要一步,快速开始

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

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

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

6

主题

3

听众

48

积分

升级  45.26%

该用户从未签到

新人进步奖

跳转到指定楼层
1#
发表于 2004-4-29 10:22 |只看该作者 |倒序浏览
|招呼Ta 关注Ta
<>一、预备知识—程序的内存分配
" m# y* U4 _# E# B2 i: z% r4 ~一个由c/C++编译的程序占用的内存分为以下几个部分0 t$ e8 `, x. I, [5 R* C: n6 q
1、栈区(stack)—   由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。- r- u& H! k; X
2、堆区(heap) —   一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
$ j" |- L- I1 Y3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放 # g" d2 Z( @/ t( o5 }
4、文字常量区  —常量字符串就是放在这里的。 程序结束后由系统释放
& B3 R8 W0 ^  F5、程序代码区—存放函数体的二进制代码。</P>
0 g; u! z3 ~4 x1 P6 ~0 M: \# I<>二、例子程序7 T; Q( Y, \$ I1 Y3 J: h
这是一个前辈写的,非常详细
' H7 x% T, ^: n' r1 d+ i; m& s//main.cpp
% D9 }& y' E1 J2 _3 Yint a = 0;    全局初始化区 3 r4 z. g/ R: o0 r1 A
char *p1;     全局未初始化区 # P/ H, Z; i1 i8 {+ _3 e2 |8 f
main()
: s" V* ]; g$ @" t{
2 Y0 ]* \/ I6 c+ X" T- ]% ~1 l- f- _int b;              栈
+ s) {$ y5 n; v- vchar s[] = "abc";   栈 * v# N$ T4 d  h: ?. F. K
char *p2;            栈   y# \$ }/ N" G2 s/ j
char *p3 = "123456";     123456\0在常量区,p3在栈上。 7 }' \$ }. L* T. b) [. e) j( A
static int c =0;       全局(静态)初始化区 $ X! M9 S  {5 L, q& a2 y2 R
p1 = (char *)malloc(10);
5 {3 R$ r+ P* d0 [$ W) {p2 = (char *)malloc(20); 3 b+ A) Q3 m* J' e2 s
分配得来得10和20字节的区域就在堆区。 % L' z( `. g4 G& \, `- [0 C
strcpy(p1, "123456");  123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。
1 F, z' F1 b3 s/ m+ H}
6 @8 b. k: W( w8 R8 W</P>3 V. k& K* l+ w2 x% i( `) i
<>二、堆和栈的理论知识
. E9 E( Q) S3 P7 i7 T8 r& G/ r2.1申请方式# B9 i- O! o( x$ K4 g5 m
  stack:$ F1 s  R' T2 e! ~6 H+ j- r$ Q. d: r
     由系统自动分配。 例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间             8 B- @$ V6 ?( }: i' t' _. Q& S
   heap:
$ _1 \! g0 d* p6 F5 Q5 G  H$ i% m# `     需要程序员自己申请,并指明大小,在c中malloc函数; ^# e. h) ^/ V. H' P, ^
       如p1 = (char *)malloc(10);
: E2 m/ x9 \- }( W     在C++中用new运算符5 a5 v; j) a0 [  p5 K
       如p2 = (char *)malloc(10); 6 J& v# H% J9 y) A4 l' h
      但是注意p1、p2本身是在栈中的。
- @$ a4 Z* ?* t: V! F: `8 o</P>
  M1 J- F9 i5 _  y/ K<>2.27 ~* f" R) S: l1 x( a
申请后系统的响应4 q% w+ a# v0 g& R  u( Q; q6 t
   栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。4 e# p6 ?. s. W- |1 o' I
   堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,0 o0 G7 x" _0 B, a  Y4 Z4 ]
会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中</P>1 m' M9 [+ s7 Y/ ?0 K9 O
, ]& d! S, F( e
<>2.3申请大小的限制) C7 L) r% s2 i' C
   栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
3 n% i  F- C. E' A- k4 P   堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。9 D2 s: W$ M2 |
</P>+ Y: r3 O6 K) ^" O9 _& _) Y; C
<>2.4申请效率的比较:
- s) h% P' V/ o   栈由系统自动分配,速度较快。但程序员是无法控制的。
" N8 @" z; F  C7 k% q2 B   堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.5 k/ s3 \- _9 C2 j2 w
   另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是直接在进程的地址空间中保留一快内存,虽然用起来最不方便。但是速度快,也最灵活。</P>
5 w0 ]2 R2 G2 y5 w) `1 O' I<>2.5堆和栈中的存储内容
) m8 ~( k/ Z3 L2 a7 ~   栈: 在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。- Q/ \$ O& I9 ]+ f' [5 ~
   当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。
  W9 c! M3 c  D/ o2 l6 O   堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。</P>6 i  h2 Z/ S6 M% j1 D2 f( }
8 Y8 r5 s5 t8 e/ E* E$ Q; h( p
<>2.6存取效率的比较</P>
, i+ B" U6 @& V: S<>char s1[] = "aaaaaaaaaaaaaaa";
0 I. k7 w  g0 Y: p/ [& c% echar *s2 = "bbbbbbbbbbbbbbbbb";
: I* i$ z4 q/ L) j) A8 E" _aaaaaaaaaaa是在运行时刻赋值的;
! j" Y4 w; ?" M而bbbbbbbbbbb是在编译时就确定的; 4 p  v6 s7 c% E
但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。
1 J1 h- D" N2 ]" L; n) T比如: ; d5 W! v. x+ z. p6 \
#include &lt;stdio.h&gt;
- z3 h, E: K: Q+ i: mvoid main() & A+ f1 D7 W' N
{ $ H9 A" \2 Q! N; z% S8 @
char a = 1; ) h- G/ P% T% P8 W* B# d' l
char c[] = "1234567890";
9 J" T& L9 q3 Q  lchar *p ="1234567890";
3 e- H8 i4 v3 f+ c; y8 ]a = c[1];
' r( w4 p, G1 a# |a = p[1]; ; m; X4 z- ^8 Z4 t' I
return; 9 V& A+ K7 Y7 p! E: c: y0 s' Q( Z
} " `8 A& Q" ?8 H# a1 E/ e
对应的汇编代码
% r: v) P, n' E10:   a = c[1];   c, e: ?' j5 U% R
00401067 8A 4D F1 mov       cl,byte ptr [ebp-0Fh]
1 {- r2 R( K6 j0040106A 88 4D FC mov         byte ptr [ebp-4],cl
- n' B: }& B8 z0 B11:   a = p[1]; + e  Z/ A8 ~1 l1 \( B5 _
0040106D 8B 55 EC mov         edx,dword ptr [ebp-14h] % t5 g2 u* R" [# ~
00401070 8A 42 01 mov       al,byte ptr [edx+1]
- M3 P+ Q, J( H# ?* H00401073 88 45 FC mov         byte ptr [ebp-4],al
6 i/ n5 C9 ]# c2 E2 m第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指针值读到edx中,在根据edx读取字符,显然慢了</P># }2 X1 A* \; J) J+ ]

: y3 I7 e/ }% J4 U5 K<>2.7小结:
7 w' @2 d( d/ ]7 ~   堆和栈的区别可以用如下的比喻来看出:  B4 d- {0 U5 K8 |! I
   使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。! `  r+ i3 r4 F$ o: d0 E* 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-6-11 22:21 , Processed in 0.414556 second(s), 57 queries .

    回顶部