QQ登录

只需要一步,快速开始

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

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

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

6

主题

3

听众

48

积分

升级  45.26%

该用户从未签到

新人进步奖

跳转到指定楼层
1#
发表于 2004-4-29 10:22 |只看该作者 |倒序浏览
|招呼Ta 关注Ta
<>一、预备知识—程序的内存分配
& Z# O0 g, j4 ?2 E+ e& [: a一个由c/C++编译的程序占用的内存分为以下几个部分" \# f! O! h+ N( @0 X: a. i
1、栈区(stack)—   由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。) e: X% S# O' x9 a* L
2、堆区(heap) —   一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。- N1 V4 O  e8 u* E$ B, g% b6 Z
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放   p5 W" h4 a% F) \  W; G
4、文字常量区  —常量字符串就是放在这里的。 程序结束后由系统释放
4 O) n7 {, R5 `6 Q  U6 Z" W5、程序代码区—存放函数体的二进制代码。</P>4 U7 O$ {# C' W  A! G
<>二、例子程序4 q9 @7 E- m0 }
这是一个前辈写的,非常详细9 j' ^" q  \* f. }
//main.cpp# @$ j; j( ^$ N/ h6 c
int a = 0;    全局初始化区 * a3 U4 X8 j/ U2 K' c
char *p1;     全局未初始化区 # E8 r- n" E2 }- y8 e: v; S; g2 T
main() % I3 S0 ~7 y& z0 ~! Y. w8 a
{ % e: n8 f! F! Z1 Y8 I+ P& a9 s
int b;              栈 9 c& O, e# a/ d' U0 \5 ~/ G7 n
char s[] = "abc";   栈 $ \. i. J' C, e: k+ C
char *p2;            栈 ) @& v* Q. T6 {7 }4 x2 f
char *p3 = "123456";     123456\0在常量区,p3在栈上。
. S- T! E1 U) D0 D0 S! \5 cstatic int c =0;       全局(静态)初始化区
% i0 g% c4 L7 L1 m& c/ a; Yp1 = (char *)malloc(10); 2 E: ], `5 D$ K
p2 = (char *)malloc(20);
  e+ B9 H  f% f8 o2 k6 r# h( v分配得来得10和20字节的区域就在堆区。 & ]( y* a* E8 n4 l/ I4 F# r
strcpy(p1, "123456");  123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。
( ]: n" ?) l" E2 {4 @: m5 |} + _! p: H. o$ c+ H7 G1 k; Y2 h
</P>: t1 I0 U) ]& J
<>二、堆和栈的理论知识
/ p$ w( F9 c4 L2 z# V; `5 G( g/ N2.1申请方式
# y6 ^% {( \. d: w2 C% s, n8 {  stack:
$ Y: b+ K2 ?) A: ^     由系统自动分配。 例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间             % N9 s3 a2 l3 W
   heap:* [# |, Z# s# q. B
     需要程序员自己申请,并指明大小,在c中malloc函数8 }$ v) g6 J! T) ?  }/ X( S5 V
       如p1 = (char *)malloc(10);   g+ e. A* v, u% U+ q
     在C++中用new运算符
1 h+ }2 K$ L. V       如p2 = (char *)malloc(10);
2 e: Y8 S3 P! ]4 y& P! A# j      但是注意p1、p2本身是在栈中的。
" h- c) _* D) v1 g; H) E</P>/ _' G! F) O! g; `5 X* x
<>2.2
' t. q7 D) C9 ~0 t, g# p0 C申请后系统的响应$ _5 f2 z. \0 a+ [' V5 z2 t- Q
   栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。7 v% q9 p% w( ?6 U
   堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,5 ^+ @% v0 e3 ]
会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中</P>
) k! O  x1 C. M# A9 ~7 O/ Q! i# _- ~( |8 S( V' g
<>2.3申请大小的限制4 T8 a$ n" A: {
   栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
2 J+ L. u& P5 N* D+ ?   堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。$ Q1 v( b* x7 E# [0 R
</P>6 f2 x! e" B) |7 U
<>2.4申请效率的比较:' V* s; U3 `* V
   栈由系统自动分配,速度较快。但程序员是无法控制的。
' z/ D% z1 o8 Q   堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.8 G8 m3 I; M. U2 P3 T: h
   另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是直接在进程的地址空间中保留一快内存,虽然用起来最不方便。但是速度快,也最灵活。</P>9 H" K( V7 z- k1 L. G" R( e- Y8 c
<>2.5堆和栈中的存储内容* R# A. a- s: c
   栈: 在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。
* _4 k& t& [4 @1 c7 \   当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。
. h$ O- {. Z6 S+ J$ C9 H) F   堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。</P>7 Y8 Y8 m0 y! S; T; R- A
$ y' n3 i% L) t1 n3 j- M
<>2.6存取效率的比较</P>
1 X+ n/ S5 _0 k7 ~, W/ g<>char s1[] = "aaaaaaaaaaaaaaa";
% A7 S. J+ y* T3 T7 ]% X: I" o2 b3 Qchar *s2 = "bbbbbbbbbbbbbbbbb"; 9 N) e$ K: L; W. r& S; \  ~8 b6 m
aaaaaaaaaaa是在运行时刻赋值的;1 \  x/ z& s' x" C: [! K/ t
而bbbbbbbbbbb是在编译时就确定的; ) k% s; U: T$ A
但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。 9 ~2 m+ [: |1 M/ j
比如: : L. q  `, e& s$ i/ ~9 K
#include &lt;stdio.h&gt;
7 b: Z) [6 M: o& {" \$ h" o& qvoid main() & P* k) V/ [/ f3 K+ D
{ * D" ?* e  A$ F0 x" S
char a = 1; " p0 `# {- x0 Y& m7 A. v. p( B! c. d
char c[] = "1234567890"; $ r. E5 \9 Z( |+ |: t' h
char *p ="1234567890"; 5 _. d7 i9 ^5 u& d- V' i7 A2 z
a = c[1]; 2 h, p* Q1 S/ e, A: z
a = p[1];
  z7 r! O1 X8 Q5 n! v, @return; 6 F" y5 _! n. H1 z0 j  @0 F: }
}
; j8 a- T% E+ w* [9 y2 w对应的汇编代码
3 Q0 K* I3 ?; d2 n+ P, Q10:   a = c[1]; % C; ~6 @$ P/ y+ Q) |  f4 X8 v
00401067 8A 4D F1 mov       cl,byte ptr [ebp-0Fh] ! ~& B# g$ N" W: X# U
0040106A 88 4D FC mov         byte ptr [ebp-4],cl
+ `$ F; h$ \- d11:   a = p[1];
" x, w0 Q/ O! U5 s8 \0040106D 8B 55 EC mov         edx,dword ptr [ebp-14h] & w  d, Z6 o) `2 B7 |' }' P
00401070 8A 42 01 mov       al,byte ptr [edx+1]
  M7 f* l9 d1 F% c& w5 x/ \00401073 88 45 FC mov         byte ptr [ebp-4],al
6 D; z; c" U3 R* Y8 n& q第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指针值读到edx中,在根据edx读取字符,显然慢了</P>
0 p: M/ n/ v% b1 s9 n) {: E* H
5 b4 j4 g+ p# f' f7 C$ o4 P<>2.7小结:
' I7 d: K9 Q6 d" Y4 N   堆和栈的区别可以用如下的比喻来看出:- Q. r& @/ c+ f4 Y" V
   使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。
0 O1 Y3 Y* C) Z- b    使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。</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 11:49 , Processed in 0.511101 second(s), 58 queries .

    回顶部