- 在线时间
- 0 小时
- 最后登录
- 2004-8-1
- 注册时间
- 2004-4-27
- 听众数
- 3
- 收听数
- 0
- 能力
- 0 分
- 体力
- 110 点
- 威望
- 0 点
- 阅读权限
- 20
- 积分
- 48
- 相册
- 0
- 日志
- 0
- 记录
- 0
- 帖子
- 6
- 主题
- 6
- 精华
- 1
- 分享
- 0
- 好友
- 0
升级   45.26% 该用户从未签到
 |
< >一、预备知识—程序的内存分配8 ~8 c7 ?! w4 C1 |2 R3 Y9 a
一个由c/C++编译的程序占用的内存分为以下几个部分6 B9 d& K! b; l% d1 w# N( n: E
1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
% r# |/ @5 K' i* s! W: l2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。; W. { ^- J$ x a+ ^
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放
( n: z# ^5 ?! L4 X- |; j4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放
" k* v' @" c- `/ P$ D- I; t5、程序代码区—存放函数体的二进制代码。</P>6 C7 e$ c6 p3 ]3 q7 @1 x5 t
< >二、例子程序% j$ a2 N' i: n- {
这是一个前辈写的,非常详细
D+ Y u/ E( {/ r' T9 D//main.cpp
4 o/ B0 X% ~$ v0 v5 e. Zint a = 0; 全局初始化区
% q$ j. P. E& d" v8 t% {4 S4 mchar *p1; 全局未初始化区 0 w5 h5 T6 r9 p6 E+ k% J' G
main() ! E$ W2 ^2 K* P1 k3 {
{ , R5 d: }/ x; v( B# k5 k8 Y! b: G
int b; 栈
5 ^0 { A# s! h, N3 a' |$ kchar s[] = "abc"; 栈
9 T" }% I$ P1 g3 B7 b$ ]+ dchar *p2; 栈
# m& V7 i; u. M% C8 M) M! Ochar *p3 = "123456"; 123456\0在常量区,p3在栈上。 1 ?1 o- H, L j! s" |( m$ `- [6 Z0 _5 ~5 ^
static int c =0; 全局(静态)初始化区
% L! Y1 @& @. a$ K2 ]5 xp1 = (char *)malloc(10); 9 H) I3 f- k; l- q
p2 = (char *)malloc(20);
4 S7 I, @( S# R0 w; t8 u分配得来得10和20字节的区域就在堆区。 ' ^ x6 B. h2 {, F6 z
strcpy(p1, "123456"); 123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。
. U3 V! h7 a! A5 `}
; N6 T( W# U# j Y</P>
9 s0 i4 n0 q! ]3 ^( E6 m< >二、堆和栈的理论知识
/ {* G% ^8 s6 S- P9 `- \7 [. R2.1申请方式
5 ^6 o. ^* o2 F% v/ {; \* \ stack:
8 g- V& D* t! B0 n* I 由系统自动分配。 例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间 ) f4 {9 h- k: r( j: r3 l' {
heap:; e8 r* v0 y5 S* u6 X! H
需要程序员自己申请,并指明大小,在c中malloc函数
1 y6 s; [9 u: z+ F2 R5 n 如p1 = (char *)malloc(10);
3 m! S# t2 @; `+ m- Y1 m 在C++中用new运算符
0 S$ \4 o) }8 R9 M2 r- b9 S 如p2 = (char *)malloc(10);
* `9 s1 A: i+ Z/ c9 z* r6 [5 u 但是注意p1、p2本身是在栈中的。2 Z. ?) e7 [3 f7 R5 O5 z1 W, J2 J# [/ X
</P>2 E0 z: X7 s* v) m2 B# W# x
< >2.24 ~7 ]$ P/ T* F4 h; `+ P
申请后系统的响应
$ h& E2 v( u. Z C1 H" g 栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。) y; q8 N" N3 @* J( D* ?: y% s
堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,
! r6 {( N( o1 t1 R- B; N" \) i+ r会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中</P>/ ]) p9 |7 G& ^6 e7 Q" H
J3 [9 |9 Y3 @9 A4 `% C0 `7 P+ z< >2.3申请大小的限制8 Y8 A8 c+ s& s2 c- b% D
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。 l h: m, O$ [; A7 J9 e# G% a
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。& Z! j2 S6 w ^/ Y; H2 \
</P>" G& H4 ?! }. `0 x4 U( {
< >2.4申请效率的比较:
# T% M1 O+ k3 D$ w+ c 栈由系统自动分配,速度较快。但程序员是无法控制的。/ `# @& x6 ]0 l h9 B1 T- V
堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.! c1 {# f0 q: p; L, t% X
另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是直接在进程的地址空间中保留一快内存,虽然用起来最不方便。但是速度快,也最灵活。</P>
) f3 m! x2 Z0 o" i, U0 P( R- X< >2.5堆和栈中的存储内容
" G) w) u9 K9 _4 b. D( @ 栈: 在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。4 w5 A1 d9 [5 j5 ~
当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。% |) @/ h& Y+ P! F
堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。</P>/ d* @8 v+ Q& U
! X* s! [- v9 \" E4 m+ ]# d4 \$ N8 g< >2.6存取效率的比较</P>
4 m% ]- C7 P% Y: l/ D x8 A. ~< >char s1[] = "aaaaaaaaaaaaaaa";
$ n; y3 a! ~% j9 x5 A) O1 mchar *s2 = "bbbbbbbbbbbbbbbbb";
. x4 s7 w" `5 B `/ o" Kaaaaaaaaaaa是在运行时刻赋值的;
0 l" Y/ F ~0 E而bbbbbbbbbbb是在编译时就确定的;
5 y6 H H, v2 Y* }1 b+ O/ |2 N但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。 ' \: M7 ~3 R& G6 D7 ?( C" V
比如:
5 S6 K" y! x E3 c0 U J5 i. y# q#include <stdio.h>
V! Z" v1 q' O" Y$ P9 D7 D8 rvoid main() 8 k* N$ A. Z5 Z; |2 ]$ \
{
9 ]- X, p+ t+ n0 |/ H0 w* Gchar a = 1;
4 X9 x1 h9 Z3 x8 M, Zchar c[] = "1234567890"; " x4 f6 d# a4 U" B2 r8 N
char *p ="1234567890";
' V! b" `5 Z& xa = c[1];
8 Y! J! o9 H9 L. N5 ?a = p[1];
) T- T, @% {& h9 n* Hreturn;
' Q" E! o$ E" l9 }" j _. q}
: n& f, }$ b+ P; P5 J& G) `对应的汇编代码
; U2 J7 O" W# r% U& O. B) i10: a = c[1]; 6 J) {0 E2 z, c g
00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh] $ {& ?" Z2 H2 I" @2 i3 _
0040106A 88 4D FC mov byte ptr [ebp-4],cl
- Q" P+ d( X3 E7 Q11: a = p[1]; " C1 ~# j* ~3 C! l' j; N
0040106D 8B 55 EC mov edx,dword ptr [ebp-14h] ; b8 T- R w. Z( F" L C
00401070 8A 42 01 mov al,byte ptr [edx+1] + \7 U2 H' n6 x+ Y
00401073 88 45 FC mov byte ptr [ebp-4],al , ]* t# L B* ?1 T9 e3 o' m
第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指针值读到edx中,在根据edx读取字符,显然慢了</P>
, u$ f# Q7 [9 t% r$ Y: ^' X- f* \/ _7 H
< >2.7小结:
' Q, d5 y6 i# w5 \: R( F" { 堆和栈的区别可以用如下的比喻来看出:
) F, K; S" j+ Q6 G3 c6 B- ?4 X6 f0 \ 使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。
0 f& \7 ^: u" v; } 使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。</P> |
zan
|