Chinaunix首页 | 论坛 | 博客
  • 博客访问: 297569
  • 博文数量: 115
  • 博客积分: 1951
  • 博客等级: 上尉
  • 技术积分: 728
  • 用 户 组: 普通用户
  • 注册时间: 2007-09-26 14:05
文章分类

全部博文(115)

文章存档

2013年(4)

2012年(3)

2011年(26)

2010年(56)

2009年(26)

我的朋友

分类: C/C++

2010-03-09 16:22:45

五大内存分区
        在C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。
        栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区。里面的变量通常是局部变量、函数参数等。
        堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。
        自由存储区,就是那些由malloc等分配的内存块,他和堆是十分相似的,不过它是用free来结束自己的生命的。
        全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。
        常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改,而且方法很多,在《const的思考》一文中,我给出了6种方法)
        
明确区分堆与栈
        在bbs上,堆与栈的区分问题,似乎是一个永恒的话题,由此可见,初学者对此往往是混淆不清的,所以我决定拿他第一个开刀。
        首先,我们举一个例子:
        void   f()   {   int*   p=new   int[5];   }  
        这条短短的一句话就包含了堆与栈,看到new,我们首先就应该想到,我们分配了一块堆内存,那么指针p呢?他分配的是一块栈内存,所以这句话的意思就是:在栈内存中存放了一个指向一块堆内存的指针p。在程序会先确定在堆中分配内存的大小,然后调用operator   new分配内存,然后返回这块内存的首地址,放入栈中,他在VC6下的汇编代码如下:
        00401028       push                 14h
        0040102A       call                 operator   new   (00401060)
        0040102F       add                   esp,4
        00401032       mov                   dword   ptr   [ebp-8],eax
        00401035       mov                   eax,dword   ptr   [ebp-8]
        00401038       mov                   dword   ptr   [ebp-4],eax
        这里,我们为了简单并没有释放内存,那么该怎么去释放呢?是delete   p么?澳,错了,应该是delete   []p,这是为了告诉编译器:我删除的是一个数组,VC6就会根据相应的Cookie信息去进行释放内存的工作。
        好了,我们回到我们的主题:堆和栈究竟有什么区别?  
        主要的区别由以下几点:
        1、管理方式不同;
        2、空间大小不同;
        3、能否产生碎片不同;
        4、生长方向不同;
        5、分配方式不同;
        6、分配效率不同;
        管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory   leak。
        空间大小:一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,一般都是有一定的空间大小的,例如,在VC6下面,默认的栈空间大小是1M(好像是,记不清楚了)。当然,我们可以修改:        
        打开工程,依次操作菜单如下:Project->Setting->Link,在Category   中选中Output,然后在Reserve中设定堆栈的最大值和commit。
注意:reserve最小值为4Byte;commit是保留在虚拟内存的页文件里面,它设置的较大会使栈开辟较大的值,可能增加内存的开销和启动时间。
        碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出,在他弹出之前,在他上面的后进的栈内容已经被弹出,详细的可以参考数据结构,这里我们就不再一一讨论了。
        生长方向:对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。
        分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。
        分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。
        从这里我们可以看到,堆和栈相比,由于大量new/delete的使用,容易造成大量的内存碎片;由于没有专门的系统支持,效率很低;由于可能引发用户态和核心态的切换,内存的申请,代价变得更加昂贵。所以栈在程序中是应用最广泛的,就算是函数的调用也利用栈去完成,函数调用过程中的参数,返回地址,EBP和局部变量都采用栈的方式存放。所以,我们推荐大家尽量用栈,而不是用堆。
        虽然栈有如此众多的好处,但是由于和堆相比不是那么灵活,有时候分配大量的内存空间,还是用堆好一些。
        无论是堆还是栈,都要防止越界现象的发生(除非你是故意使其越界),因为越界的结果要么是程序崩溃,要么是摧毁程序的堆、栈结构,产生以想不到的结果,就算是在你的程序运行过程中,没有发生上面的问题,你还是要小心,说不定什么时候就崩掉,那时候debug可是相当困难的:)

+----------------------------------------------+    高地址

                                                             | <- 命令行参数和环境变量

                                                             |

|------------------------------------------------| <------- 栈底
                              栈                               |

                                                            |

                                                                    

|~~~~~~~~~~~~~~~~~~~~~~~~~~~~| <-------    栈顶

                                                             |

                                                             |

|~~~~~~~~~~~~~~~~~~~~~~~~~~~~|

                                                             |

                                                            

                              堆                               |

|-------------------------------------------------|

                      未初始化的数据                    | <-------- 由exec()赋初值0

|-------------------------------------------------|

                        初始化的数据                      |---\

|--------------------------------------------------|    \ exec()从程序文件中读

                           正文                              / 到(硬盘上的文件)

                                                             |---/

+-------------------------------------------------+   低地址

//---------------------------------------------------------------------

在函数体中定义的变量通常是在栈上,用malloc, calloc, realloc等分配内存的函数分配得到的就是
在堆上。在所有函数体外定义的是全局量,加了static修饰符后不管在哪里都存放在全局区(静态
区),在所有函数体外定义的static变量表示在该文件中有效,不能extern到别的文件用,在函数体内
定义的static表示只在该函数体内有效。另外,函数中的"adgfdf"这样的字符串存放在常量区。
比如:
代码:

int a = 0; //全局初始化区
char *p1; //全局未初始化区
main()
{
int b; //栈
char s[] = "abc"; //栈
char *p2; //栈
char *p3 = "123456"; //123456\0在常量区,p3在栈上。
static int c = 0; //全局(静态)初始化区
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);
//分配得来得10和20字节的区域就在堆区。
strcpy(p1, "123456");
//123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一块。
}


还有就是函数调用时会在栈上有一系列的保留现场及传递参数的操作。
栈的空间大小有限定,vc的缺省是2M。栈不够用的情况一般是程序中分配了大量数组和递归函数层次太
深。有一点必须知道,当一个函数调用完返回后它会释放该函数中所有的栈空间。栈是由编译器自动管
理的,不用你操心。
堆是动态分配内存的,并且你可以分配使用很大的内存。但是用不好会产生内存泄漏。
并且频繁地malloc和free会产生内存碎片(有点类似磁盘碎片),因为c分配动态内存时是寻找匹配的
内存的。而用栈则不会产生碎片。
在栈上存取数据比通过指针在堆上存取数据快些。
一般大家说的堆栈和栈是一样的,就是栈(stack),而说堆时才是堆heap.
栈是先入后出的,一般是由高地址向低地址生长。
转载的另外一篇:

堆(heap)和栈(stack)是C/C++编程不可避免会碰到的两个基本概念。首先,这两个概念都可以在讲数据
结构的书中找到,他们都是基本的数据结构,虽然栈更为简单一些。
在具体的C/C++编程框架中,这两个概念并不是并行的。对底层机器代码的研究可以揭示,栈是机器系
统提供的数据结构,而堆则是C/C++函数库提供的。
具体地说,现代计算机(串行执行机制),都直接在代码底层支持栈的数据结构。这体现在,有专门的寄
存器指向栈所在的地址,有专门的机器指令完成数据入栈出栈的操作。
这种机制的特点是效率高,支持的数据有限,一般是整数,指针,浮点数等系统直接支持的数据类型,
并不直接支持其他的数据结构。因为栈的这种特点,对栈的使用在程序中是非常频繁的。对子程序的调
用就是直接利用栈完成的。机器的call指令里隐含了把返回地址推入栈,然后跳转至子程序地址的操
作,而子程序中的ret指令则隐含从堆栈中弹出返回地址并跳转之的操作。C/C++中的自动变量是直接利
用栈的例子,这也就是为什么当函数返回时,该函数的自动变量自动失效的原因。

和栈不同,堆的数据结构并不是由系统(无论是机器系统还是操作系统)支持的,而是由函数库提供的。
基本的malloc/realloc/free函数维护了一套内部的堆数据结构。当程序使用这些函数去获得新的内存
空间时,这套函数首先试图从内部堆中寻找可用的内存空间,如果没有可以使用的内存空间,则试图利
用系统调用来动态增加程序数据段的内存大小,新分配得到的空间首先被组织进内部堆中去,然后再以
适当的形式返回给调用者。当程序释放分配的内存空间时,这片内存空间被返回内部堆结构中,可能会
被适当的处理(比如和其他空闲空间合并成更大的空闲空间),以更适合下一次内存分配申请。这套复杂
的分配机制实际上相当于一个内存分配的缓冲池(Cache),使用这套机制有如下若干原因:

1. 系统调用可能不支持任意大小的内存分配。有些系统的系统调用只支持固定大小及其倍数的内存请
求(按页分配);这样的话对于大量的小内存分类来说会造成浪费。

2. 系统调用申请内存可能是代价昂贵的。系统调用可能涉及用户态和核心态的转换。

3. 没有管理的内存分配在大量复杂内存的分配释放操作下很容易造成内存碎片。

堆和栈的对比

从以上知识可知,栈是系统提供的功能,特点是快速高效,缺点是有限制,数据不灵活;而栈是函数库
提供的功能,特点是灵活方便,数据适应面广泛,但是效率有一定降低。栈是系统数据结构,对于进
程/线程是唯一的;堆是函数库内部数据结构,不一定唯一。不同堆分配的内存无法互相操作。栈空间
分静态分配和动态分配两种。静态分配是编译器完成的,比如自动变量(auto)的分配。动态分配由
alloca函数完成。栈的动态分配无需释放(是自动的),也就没有释放函数。为可移植的程序起见,栈的
动态分配操作是不被鼓励的!堆空间的分配总是动态的,虽然程序结束时所有的数据空间都会被释放回
系统,但是精确的申请内存/释放内存匹配是良好程序的基本要素。

//---------------------------------------------------------------------

堆和栈
在win32和 win16 中 ,堆   (heap)   是指可供分配的内存 ,进程中可动态分配。  

在传统的c 中堆和栈实际是一块物理内存 ,堆主要用来动态分配内存,从堆栈内存的低端向上分配;   而栈主要用来传递函数参数、返回值和局部参数内存分配,是从堆栈内存的高端向下分配,俗称压栈和出栈。  

一般的子程序的入参及局部变量都放在栈中的,子程序结束后 ,栈中的局部变量将消失 ;

//---------------------------------------------------------------------

栈(Stack)和堆(Heap)是两个不同层次上的概念。
栈应该是在CPU一级的概念,即栈对CPU是可见的,每一个基本的调度单位(进程或线程)都有自己独立的栈,供上下文切换和局部变量内存空间分配使用。
堆是一个应用层的概念,即堆对CPU是不可见的,它的实现方式有多种,可以由OS实现,也可以由运行库实现,如果你愿意,你也可以在一个栈中来实现一个堆:)。
有的OS的实现了比较简单的堆的调用接口,
C运行库中的堆则实现了比较复杂的堆的功能来支持动态内存的分配与释放。当然运行库中的堆是使用了OS中的内存访问的系统调用来实现的。
堆栈这个概念很容易混淆,大部分情况下指的是一个栈(Stack)

//---------------------------------------------------------------------

=========================================
heap
=========================================
In certain programming languages including C and Pas
cal, a heap is an area of pre-reserved computer main storage (memory) that a program process can use to store data in some variable amount that won't be known until the program is running. For example, a program may accept different amounts of input from one or more users for processing and then do the processing on all the input data at once. Having a certain amount of heap storage already obtained from the operating system makes it easier for the process to manage storage and is generally faster than asking the operating system for storage every time it's needed. The process manages its allocated heap by requesting a "chunk" of the heap (called a heap block) when needed, returning the blocks when no longer needed, and doing occasional "garbage collecting," which makes blocks available that are no longer being used and also reorganizes the available space in the heap so that it isn't being wasted in small unused pieces.
The term is apparently inspired by another term, stack. A stack is similar to a heap except that the blocks are taken out of storage in a certain order and returned in the same way. In Pascal, a subheap is a portion of a heap that is treated like a stack.
=========================================
stack
========================================
(1) TCP/IP is frequently referred to as a "stack." This refers to the layers (TCP, IP, and sometimes others) through which all data passes at both client and server ends of a data exchange. A clear picture of layers similar to those of TCP/IP is provided in our description of OSI, the reference model of the layers involved in any network communication.

The term "stack" is sometimes used to include utilities that support the layers of TCP/IP. The Netscape Handbook says (and we quote): "To make a successful connection to the Internet, your PC needs application software such as Netscape plus a TCP/IP stack consisting of TCP/IP software, sockets software (Winsock.dynamic link library), and hardware driver software (packet drivers). Several popular TCP/IP stacks are available for Windows, including shareware stacks."

(2) In programming, a stack is a data area or buffer used for storing requests that need to be handled. The IBM Dictionary of Computing says that a stack is always a push-down list, meaning that as new requests come in, they push down the old ones. Another way of looking at a push-down list - or stack - is that the program always takes its next item to handle from the top of the stack. (This is unlike other arrangements such as "FIFO" or "first-in first-out.")

 

也可参看某位国外学生写的读书笔记,讲得很详细:http://www.cs.jcu.edu.au/Subjects/cp2003/1997/foils/heapAndStack/heapAndStack.html


 

阅读(1273) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~