- 在线时间
- 0 小时
- 最后登录
- 2004-8-1
- 注册时间
- 2004-4-27
- 听众数
- 3
- 收听数
- 0
- 能力
- 0 分
- 体力
- 110 点
- 威望
- 0 点
- 阅读权限
- 20
- 积分
- 48
- 相册
- 0
- 日志
- 0
- 记录
- 0
- 帖子
- 6
- 主题
- 6
- 精华
- 1
- 分享
- 0
- 好友
- 0
升级   45.26% 该用户从未签到
 |
< >一、预备知识—程序的内存分配
& 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 <stdio.h>
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
|