QQ登录

只需要一步,快速开始

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

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

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

6

主题

3

听众

48

积分

升级  45.26%

该用户从未签到

新人进步奖

跳转到指定楼层
1#
发表于 2004-4-29 10:22 |只看该作者 |正序浏览
|招呼Ta 关注Ta
<>一、预备知识—程序的内存分配* a( B7 N  i* U4 E- w
一个由c/C++编译的程序占用的内存分为以下几个部分5 L+ ]) h9 _: b4 }+ ?4 n% W' x
1、栈区(stack)—   由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。, L. K! u! B0 D# G! n7 t3 H
2、堆区(heap) —   一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。: m( X' J- o7 }) e: Q
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放 9 s3 O5 l( j0 C3 i
4、文字常量区  —常量字符串就是放在这里的。 程序结束后由系统释放
9 p' M7 t% B( G" N3 ?( k5、程序代码区—存放函数体的二进制代码。</P>( S5 h1 O1 q5 ]: K% d% n! c/ S
<>二、例子程序
9 x3 a  c8 E4 b7 a( C8 S这是一个前辈写的,非常详细5 ^  K, d' K+ t! }" e! U
//main.cpp
7 E, Y' [7 l* z/ i4 A: fint a = 0;    全局初始化区
. ^! |$ q0 r/ a3 x( ychar *p1;     全局未初始化区
5 Y- Y9 @- s  Q; o% c* pmain()   X5 s1 P6 ^8 h, \
{ " Y0 l! l7 {( ], X
int b;              栈
0 j0 L) k7 G1 S0 z1 vchar s[] = "abc";   栈
9 A  x0 S2 u' U6 Tchar *p2;            栈 1 X$ v' R4 A, U9 z. P( A
char *p3 = "123456";     123456\0在常量区,p3在栈上。
1 A3 H/ c! I3 }$ w) o/ H0 Ustatic int c =0;       全局(静态)初始化区
5 ]' z( u# }1 ^9 P- g) _, \6 ap1 = (char *)malloc(10);
2 L% I- p8 t( k1 `$ }- j7 a! |p2 = (char *)malloc(20);
* k3 ^2 `* d" j+ A6 @! {分配得来得10和20字节的区域就在堆区。
& |) h" P1 E$ {6 }( n  Y/ ], _, Qstrcpy(p1, "123456");  123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。 1 @! e+ j) [+ \/ r( d1 d; }5 M
}
4 F/ H9 z/ x) V' I</P>
% N( I, C1 ^- \  N<>二、堆和栈的理论知识- i! W; x% v) i$ Y  I/ [7 W
2.1申请方式
3 i0 |8 X. L8 Z/ \3 j; i  stack:3 _( n% N( o  I! p  S
     由系统自动分配。 例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间            
7 ~! ^9 i- z, I5 U# M   heap:' q, {& f  _; y4 o6 q
     需要程序员自己申请,并指明大小,在c中malloc函数$ E7 b: e0 D) f# f; x
       如p1 = (char *)malloc(10);
9 Y8 \# t5 z8 v& y# i; V4 E     在C++中用new运算符2 j% I0 t  K( I
       如p2 = (char *)malloc(10);
- @/ l* w2 A4 g4 o" e8 q      但是注意p1、p2本身是在栈中的。
! }, d0 N- O- w( {& v: A) w</P>$ r% j# B, ?% P
<>2.2
) E. P5 {: h. Z3 H3 a9 g/ C5 A申请后系统的响应* g, D# I6 X6 {3 ?9 |7 ]
   栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
7 S/ G5 E4 v+ V' d! b1 W" `   堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,
. d; v! ]" Q! }4 u# U6 \会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中</P>
* J2 ?/ u( ?& U, e+ Y1 Q" D! H! e8 B% [& S" m
<>2.3申请大小的限制
6 a# D8 e; P( A% s- D& R/ V/ R   栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
7 l9 x5 N& m- @; i. d   堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
$ L. @, F( _5 x2 _0 `</P>* V: c6 e# V; L6 O3 m' h
<>2.4申请效率的比较:
6 [8 ]' |7 A. c* l   栈由系统自动分配,速度较快。但程序员是无法控制的。( a6 k% @$ t% i1 H. |
   堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.; H: F( S% R- {: e; x$ U
   另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是直接在进程的地址空间中保留一快内存,虽然用起来最不方便。但是速度快,也最灵活。</P>/ M: [; C! x' ]5 |( d4 p6 V
<>2.5堆和栈中的存储内容! Z! o# h4 @5 @3 E5 F+ z
   栈: 在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。% W! I2 N! K; o$ S' i( t7 ^1 E- G
   当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。9 M! n% e# G* N- y* ]
   堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。</P>
+ O5 ~+ L* P& t; o# Q2 ?) V" |. D! w, s, }
<>2.6存取效率的比较</P>$ x9 u+ Y: l$ [# m' A$ H* K
<>char s1[] = "aaaaaaaaaaaaaaa"; 1 g9 u: X4 w- O6 E
char *s2 = "bbbbbbbbbbbbbbbbb";
- K- Y4 C6 u8 h7 A! caaaaaaaaaaa是在运行时刻赋值的;& f4 o# ]% Q2 {$ W3 a. T4 V2 q6 b
而bbbbbbbbbbb是在编译时就确定的; 8 ]  b2 ~' L' |- ?* D3 g3 }
但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。 * e; x" a7 W: Z1 G
比如: 7 Q2 X7 C6 C* f% D; D5 a+ q* ^
#include &lt;stdio.h&gt;
1 |' W8 g* T+ O# O9 C' Y: `void main()
  l) h- Y! o  L2 b& V{
- P% U1 ]1 i2 _! B/ z  D4 c; B& Z1 ochar a = 1; 4 l7 F$ a; R+ E* B! H2 ]
char c[] = "1234567890"; ( l2 s. [  ^. J3 p
char *p ="1234567890";
% ]% B) M) N' Z( ~a = c[1]; + j6 V$ \+ {6 z7 g: P
a = p[1]; . W% x" n5 f- {0 t& t: [1 w
return; " x8 m8 l3 ~+ I; w( c
}
1 z& W7 n7 Y2 k/ q2 H5 a3 `对应的汇编代码
4 ]6 I9 r! R$ c' v8 u7 o( t$ d10:   a = c[1];
8 u3 a3 o+ R- s4 ^- U00401067 8A 4D F1 mov       cl,byte ptr [ebp-0Fh] 3 k  e  Q7 K( c' [# o
0040106A 88 4D FC mov         byte ptr [ebp-4],cl 6 x8 K7 Q) y$ F- s5 C7 Y- Z+ t; |
11:   a = p[1]; ! g" U9 x. U) x6 [; Z) }( b  m
0040106D 8B 55 EC mov         edx,dword ptr [ebp-14h] 4 a) `& D4 \7 y8 c& I
00401070 8A 42 01 mov       al,byte ptr [edx+1]
3 H0 T1 q8 |' C" }5 T00401073 88 45 FC mov         byte ptr [ebp-4],al
9 E4 d) V4 ?: R; p' q5 E8 B+ j第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指针值读到edx中,在根据edx读取字符,显然慢了</P>. v8 {/ W6 y; q/ {

* c, K7 i( r+ H+ w% c<>2.7小结:
7 s) ~' ^0 d, i9 I   堆和栈的区别可以用如下的比喻来看出:
" J8 T9 _0 E, Y   使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。8 l6 ]  T% F$ u1 p
    使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。</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-4-20 17:03 , Processed in 0.451609 second(s), 59 queries .

    回顶部