QQ登录

只需要一步,快速开始

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

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

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

6

主题

3

听众

48

积分

升级  45.26%

该用户从未签到

新人进步奖

跳转到指定楼层
1#
发表于 2004-4-29 10:22 |只看该作者 |倒序浏览
|招呼Ta 关注Ta
<>一、预备知识—程序的内存分配
$ ~0 j2 Z/ I; R  m) W+ x一个由c/C++编译的程序占用的内存分为以下几个部分7 b2 G) {  J! f8 Q! g: {* o  l  ?
1、栈区(stack)—   由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。* J. E( ^: J/ S# ]8 d3 M8 ]
2、堆区(heap) —   一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
/ {9 Q( e3 n) W% ?3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放 8 d1 D& a8 |- K# C  ?/ C4 \
4、文字常量区  —常量字符串就是放在这里的。 程序结束后由系统释放
9 x" O7 W; L0 z/ b, H, G- _( J5、程序代码区—存放函数体的二进制代码。</P>2 ~) Q; }4 U' g
<>二、例子程序* G3 ]$ n* h* q8 `0 r5 `4 B
这是一个前辈写的,非常详细
& Y$ E2 m6 W) L//main.cpp
4 I, E/ N, G0 \% |; ?% i$ `3 U  M6 _  qint a = 0;    全局初始化区 ! b  r2 j5 f" }; r. L
char *p1;     全局未初始化区 3 g! B4 s* d1 B6 K+ b% {: {
main()
9 y* ?0 o# f. ?* ^4 n: P" u1 i{ 7 u* G6 e; e0 i/ Q4 Y# E! ?
int b;              栈
" B7 o+ O9 D' |4 R; i3 C0 Tchar s[] = "abc";   栈
% `% D' I7 T9 I6 mchar *p2;            栈 - }1 a/ I  t) A7 \1 f& \  R
char *p3 = "123456";     123456\0在常量区,p3在栈上。 0 x' q  J" s: A0 S6 @
static int c =0;       全局(静态)初始化区
( F2 v  {1 p2 a* C9 e% Dp1 = (char *)malloc(10);
# v" j8 L2 N$ |1 M  `! m$ `" ip2 = (char *)malloc(20); & n0 h% T6 P6 d/ @- b. M$ K
分配得来得10和20字节的区域就在堆区。 % @  g5 Q, L8 Z6 V0 _
strcpy(p1, "123456");  123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。 ( v( y) \! i5 p6 [5 w# {" ^
} ; ]3 ~$ R) \/ |5 ^7 c' x: Q
</P>
! {; j  b5 z; Y<>二、堆和栈的理论知识- C' D. L4 ~1 w' d. }$ l: v7 p
2.1申请方式/ w+ T6 r& D1 q, ~
  stack:
! G% C( _8 {. K  @     由系统自动分配。 例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间            
. x$ U: M( a6 F( D   heap:) r: J) w) `+ Y$ q( d, X) a
     需要程序员自己申请,并指明大小,在c中malloc函数# d: ~) s- w, B9 o  j3 K
       如p1 = (char *)malloc(10); ' h: V6 s. E4 K" A' C* C
     在C++中用new运算符
, c( K& C4 k* G7 {3 b- i5 ~       如p2 = (char *)malloc(10);
& ]% M1 V; @5 ^# o6 h+ y) w& t      但是注意p1、p2本身是在栈中的。
+ R  I* W5 C$ ^/ N9 N</P># H1 T  M5 w1 o0 p
<>2.2+ P$ g! @+ v. H) D
申请后系统的响应
# N7 {  B% m$ o2 ^) l2 j   栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
  B+ K" p: u* Y   堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,
" o) ^& y4 U" u: B: f会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中</P>2 p! n& W% P$ u/ |- D
/ V4 ^& V! z( L9 o
<>2.3申请大小的限制
- F5 x" d& w5 I- R' Z5 u" f# z   栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。9 ~! F, I& m; E( V2 {) u+ p
   堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
, c' l( ?7 e+ v. Z</P>: W8 x6 Y+ p3 z+ j. E' y1 W
<>2.4申请效率的比较:* z2 F8 Z- `0 t5 ?$ U
   栈由系统自动分配,速度较快。但程序员是无法控制的。
% c0 j/ p3 e* j; n   堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.4 h- t, ?3 G" F
   另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是直接在进程的地址空间中保留一快内存,虽然用起来最不方便。但是速度快,也最灵活。</P>! p( Y! ]0 E+ b
<>2.5堆和栈中的存储内容" w2 D" \, }* N1 t7 z. n
   栈: 在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。& ?1 ]2 R, U$ [. y- P
   当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。
  H! s" h2 ?# ~7 {4 P  n; w) Y" o1 L   堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。</P>
, ~$ j* r8 y6 p
; a$ Z  k& R$ ~<>2.6存取效率的比较</P>
! `: K4 p! `0 `! Q/ y9 m<>char s1[] = "aaaaaaaaaaaaaaa"; 1 N  w) u$ f. ~) x
char *s2 = "bbbbbbbbbbbbbbbbb";
) B6 a, U* D5 H( b/ k: v0 {& waaaaaaaaaaa是在运行时刻赋值的;' \2 i( g8 p) [3 X- C8 t
而bbbbbbbbbbb是在编译时就确定的;
0 Y. u2 Q. ?2 ~但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。
2 E6 l8 `1 D: N! D$ c比如: ! O: _7 M/ o4 }3 E/ q% M8 p- g& w! N( \
#include &lt;stdio.h&gt;
1 U0 x" o8 P7 t* G7 `8 X2 }void main() 8 \0 o: |# i3 y$ n$ e/ B
{ 9 Q; I* J+ X* h
char a = 1; 6 A9 {  b2 j2 ]
char c[] = "1234567890"; - P5 F' k3 ?& r. z2 j
char *p ="1234567890"; 2 }" u. P: s& A$ D1 v) ~1 k
a = c[1]; # u  Y, M" R$ |; a( J  e& T
a = p[1]; & b/ M( \% U2 B/ {
return; % s5 L7 X+ j! C$ V
} # _6 h% f4 @6 A( O
对应的汇编代码+ a2 \2 N8 e, b! C
10:   a = c[1];
. ~4 ~- d# G- P" O; Q! I4 W# I00401067 8A 4D F1 mov       cl,byte ptr [ebp-0Fh] $ U% E$ v  _- O( R: ?, K9 t( S
0040106A 88 4D FC mov         byte ptr [ebp-4],cl 5 K# ?+ y& ^0 L* a
11:   a = p[1]; & n2 J2 L3 K% ~/ }3 L6 C. Z
0040106D 8B 55 EC mov         edx,dword ptr [ebp-14h]
- T& }! y7 J+ _3 @! n' V00401070 8A 42 01 mov       al,byte ptr [edx+1] 9 o' l4 K0 \' ?) n+ o
00401073 88 45 FC mov         byte ptr [ebp-4],al
/ Z, K& o1 ]+ K" y/ A* n第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指针值读到edx中,在根据edx读取字符,显然慢了</P>/ @/ h$ G, Z" |  t2 S

6 o" X6 G1 e: Z! \& @4 A<>2.7小结:
. U5 M- T5 i) ^) T% t   堆和栈的区别可以用如下的比喻来看出:
0 }: I8 s" @' `' q+ @3 u7 v9 Q   使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。
' J. F+ N" r# |$ k; B) T& F    使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。</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 01:27 , Processed in 2.033814 second(s), 57 queries .

    回顶部