C++中的堆和栈

来源:互联网 发布:农业大数据应用 编辑:程序博客网 时间:2024/05/01 20:07

本文转自http://blog.csdn.net/nieyibin/article/details/7468323


C++中堆和栈的区别,自由存储区、全局/静态存储区和常量存储区       

文章来自一个论坛里的回帖,哪个论坛记不得了!  

    在C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态 
存储区和常量存储区。  
    栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量 
的存储区。里面的变量通常是局部变量、函数参数等。  
    堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应 
用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉, 
那么在程序结束后,操作系统会自动回收。  
    自由存储区,就是那些由malloc等分配的内存块,他和堆是十分相似的, 
不过它是用free来结束自己的生命的。  
    全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的 
C语言中,全局变量又分为初始化的和未初始化的(初始化的全局变量和静态变 
量在一块区域,未初始化的全局变量与静态变量在相邻的另一块区域,同时未被 
初始化的对象存储区可以通过void*来访问和操纵,程序结束后由系统自行释 
放),在C++里面没有这个区分了,他们共同占用同一块内存区。  
    常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许 
修改(当然,你要通过非正当手段也可以修改,而且方法很多)  
明确区分堆与栈  
    在 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、生长方向不同;  

----------------------- Page 2-----------------------

    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可是相当困难的:)  
    对了,还有一件事,如果有人把堆栈合起来说,那它的意思是栈,可不是堆, 
呵呵,清楚了?  

----------------------- Page 3-----------------------

static用来控制变量的存储方式和可见性  
    函数内部定义的变量,在程序执行到它的定义处时,编译器为它在栈上分配 
空间,函数在栈上分配的空间在此函数执行结束时会释放掉,这样就产生了一个 
问题: 如果想将函数中此变量的值保存至下一次调用时,如何实现? 最容易想 
到的方法是定义一个全局的变量,但定义为一个全局变量有许多缺点,最明显的 
缺点是破坏了此变量的访问范围(使得在此函数中定义的变量,不仅仅受此 函 
数控制)。  

需要一个数据对象为整个类而非某个对象服务,同时又力求不破坏类的封装性, 
即要求此成员隐藏在类的内部,对外不可见。  

static的内部机制:  
    静态数据成员要在程序一开始运行时就必须存在。因为函数在程序运行中被 
调用,所以静态数据成员不能在任何函数内分配空间和初始化。这样,它的空间 
分配有三个可能的地方,一是作为类的外部接口的头文件,那里有类声明;二是 
类定义的内部实现,那里有类的成员函数定义;三是应用程序的main()函数 
前的全局数据声明和定义处。  
    静态数据成员要实际地分配空间,故不能在类的声明中定义(只能声明数据 
成员)。类声明只声明一个类的“尺寸和规格”,并不进行实际的内存分配,所 
以在类声 明中写成定义是错误的。它也不能在头文件中类声明的外部定义,因 
为那会造成在多个使用该类的源文件中,对其重复定义。  
    static被引入以告知编译器,将变量存储在程序的静态存储区而非栈上空 
间,静态  
数据成员按定义出现的先后顺序依次初始化,注意静态成员嵌套时,要保证所嵌 
套的成员已经初始化了。消除时的顺序是初始化的反顺序。  

static的优势:  
    可以节省内存,因为它是所有对象所公有的,因此,对多个对象来说,静态 
数据成员只存储一处,供所有对象共用。静态数据成员的值对每个对象都是一样, 
但它的 值是可以更新的。只要对静态数据成员的值更新一次,保证所有对象存 
取更新后的相同的值,这样可以提高时间效率。引用静态数据成员时,采用如下 
格式:  
    <类名>::<静态成员名>  
    如果静态数据成员的访问权限允许的话(即public的成员),可在程序中, 
按上述格式  
来引用静态数据成员。  

ps:  
    (1)类的静态成员函数是属于整个类而非类的对象,所以它没有this指针, 
这就导致了它仅能访问类的静态数据和静态成员函数。  
    (2)不能将静态成员函数定义为虚函数。  
    (3)由于静态成员声明于类中,操作于其外,所以对其取地址操作,就多少 
有些特殊,变量地址是指向其数据类型的指针 ,函数地址类型是一个 
 “nonmember函数指针”。  
    (4)由于静态成员函数没有this指针,所以就差不多等同于nonmember函数, 

----------------------- Page 4-----------------------

结果就产生了一个意想不到的好处:成为一个callback函数,使得我们得以将 
c++和c-based x window系统结合,同时也成功的应用于线程函数身上。  
    (5)static并没有增加程序的时空开销,相反她还缩短了子类对父类静态成 
员的访问时间,节省了子类的内存空间。  
    (6)静态数据成员在<定义或说明>时前面加关键字static。  
    (7)静态数据成员是静态存储的,所以必须对它进行初始化。  
    (8)静态成员初始化与一般数据成员初始化不同:  
    初始化在类体外进行,而前面不加static,以免与一般静态变量或对象相 
混淆;  
    初始化时不加该成员的访问权限控制符private,public等;  
    初始化时使用作用域运算符来标明它所属类;  
    所以我们得出静态数据成员初始化的格式:  
    <数据类型><类名>::<静态数据成员名>=<值>  
    (9)为了防止父类的影响,可以在子类定义一个与父类相同的静态变量,以 
屏蔽父类的影响。这里有一点需要注意:我们说静态成员为父类和子类共享,但 
我们有 重复定义了静态成员,这会不会引起错误呢?不会,我们的编译器采用 
了一种绝妙的手法:name-mangling 用以生成唯一的标志。  

---------------------------------- 
-------------  

 【转】全局变量静态变量  

static 声明的变量在C语言中有两方面的特征:  
    1)、变量会被放在程序的全局存储区中,这样可以在下一次调用的时候还可 
以保持原来的赋值。这一点是它与堆栈变量和堆变量的区别。  
    2)、变量用static告知编译器,自己仅仅在变量的作用范围内可见。这一 
点是它与全局变量的区别。  
Tips:  
    A.若全局变量仅在单个C文件中访问,则可以将这个变量修改为静态全局变 
量,以降低模块间的耦合度;  
B.若全局变量仅由单个函数访问,则可以将这个变量改为该函数的静态局部变 
量,以降低模块间的耦合度;  
C.设计和使用访问动态全局变量、静态全局变量、静态局部变量的函数时,需要 
考虑重入问题;  
 D.如果我们需要一个可重入的函数,那么,我们一定要避免函数中使用static 
变量(这样的函数被称为:带“内部存储器”功能的的函数)  
E.函数中必须要使用static变量情况:比如当某函数的返回值为指针类型时,则 
必须是static的局部变量的地址作为返回值,若为auto类型,则返回为错指针。  

函数前加static使得函数成为静态函数。但此处“static”的含义不是指存储 
方式,而是指对函数的作用域仅局限于本文件(所以又称内部函数)。使用内部函 
数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与 
其它文件中的函数同名。  

----------------------- Page 5-----------------------

扩展分析:术语static有着不寻常的历史.起初,在C中引入关键字static是为 
了表示退出一个块后仍然存在的局部变量。随后,static在C中有了第二种含 
义:用来表示不能被其它文件访问的全局变量和函数。为了避免引入新的关键字, 
所以仍使用static关键字来表示这第二种含义。最后,C++重用了这个关键字, 
并赋予它与前面不同的第三种含义:表示属于一个类而不是属于此类的任何特定 
对象的变量和函数(与Java中此关键字的含义相同)。  

全局变量、静态全局变量、静态局部变量和局部变量的区别  
变量可以分为:全局变量、静态全局变量、静态局部变量和局部变量。  
  按存储区域分,全局变量、静态全局变量和静态局部变量都存放在内存的静态 
存储区域,局部变量存放在内存的栈区。  
 按作用域分,全局变量在整个工程文件内都有效;静态全局变量只在定义它的 
文件内有效;静态局部变量只在定义它的函数内有效,只是程序仅分配一次内存, 
函数返回后,该变量不会消失;局部变量在定义它的函数内有效,但是函数返回 
后失效。  
全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。全局 
变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。 这两者在 
存储方式上并无不同。这两者的区别虽在于非静态全局变量的作用域是整个源程 
序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都 
是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内 
有效, 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域 
局限于一个源文件内,只能为该源文件内的函数公用, 因此可以避免在其它源 
文件中引起错误。  
    从以上分析可以看出, 把局部变量改变为静态变量后是改变了它的存储方 
式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域, 限 
制了它的使用范围。  

    static函数与普通函数作用域不同。仅在本文件。只在当前源文件中使用 
的函数应该说明为内部函数(static),内部函数应该在当前源文件中说明和定 
义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这 
些函数的源文件要包含这个头文件  

  static全局变量与普通的全局变量有什么区别:static全局变量只初使化一 
次,防止在其他文件单元中被引用;  
  static局部变量和普通局部变量有什么区别:static局部变量只被初始化一 
次,下一次依据上一次结果值;  
  static函数与普通函数有什么区别:static函数在内存中只有一份,普通函 
数在每个被调用中维持一份拷贝  
  全局变量和静态变量如果没有手工初始化,则由编译器初始化为0。局部变量 
的值不可知。  

堆和栈 内存分配中堆栈和堆有什么区别呀? 

对了!这就是说动态分配内存时我们采用了 Heap, 

 静态时就可以使用stack。 

----------------------- Page 6-----------------------

  VCL 的对象因此必须使用new 来分配内存, 

  TForm   form;这样的写法在VCL 中绝对不允许。 

  下面    Copy   paste 一下 

  所有的 VCL 对象都通过指针进行引用。C++                  Builder 中不存在任何VCL 类的任何静态或局部实例,从 

表面上看,这主要与VCL 是从Object             Pascal 演变而来有关,而真正的深层次原因是由面向对象技术的关 

联与委托模型决定的。 

        基于性能和内存高效分配的原因,在堆(heap)中分配对象比在栈(stack)中分配对象的效率要高。 

因此,Delphi 最大限度地简化了语言的语法,强制用户必须在堆中分配对象。因为 Pascal 的高度机构性, 

程序员所面临的指针问题比C++要简单得多。VCL 对象的构造函数自动在堆中分配对象而不需要指针的参 

与。对于程序员来说,没有复杂指针的 Delphi 确实好用,但程序员必须记住,没有指针事实上意味着指针 

无处不在——Delphi  根本就不允许程序员创建任何一个非指针的对象!注意,所有的其他数据类型,诸如 

字符串、整数、数组和结构(记录)等,都可以生命为静态的或动态的,此项限制仅适用于对象。 

                                        例如以下代码: 

                                        var 

                                           T:   ^TObject; 

                                        begin 

                                           T   :=   New(TObject,   Create); 

                                           ........ 

                                           T^.Free; 

                                        end; 

      上述代码在 Delphi 中是不可能编译通过的。但它事实上就是 Delphi 处理对象的方式。Delphi 对对 

象声明语法的简化使程序员使用起来更简单。上述代码应改为: 

                                        var 

                                           T:   TObject; 

                                        begin 

                                           T   :=   TObject.Create; 

                                           ..... 

                                           T.Free; 

                                        end; 

        上述两段代码并不相同,按照程序员对C++的理解,它们不应该生成相同的机器码。但事实上, 

要是前一段代码在 Delphi 中能够编译通过的话,它们的机器码是完全相同的。 

    union 和 struct 的区别 
      union UTest          { 
                                 double  dlOne; 
                                char     chOne; 
                                byte     bArray[4]; 
                           }; 

             好了,看到上面的定义,很像 struct 的定义,但是对于union 来说,有几点是值 
得注意的:不能直接对其进行初始化;某个时候只能使用其中的一个元素;最后一点也是最 

----------------------- Page 7-----------------------

重要的一点就是内存共享,分配给 union  内存的Size 是其中 Size 最大的那个元素的 Size。 
说到这里,既然union 最重要的是内存共享,那么我们做如下定义:union UTest tEle;然后赋 
值:tEle.dlOne = 2.0f;现在是 dlOne 可用,下一步:tEle.chOne = 'A';到这里dlOne 失去了其 
意义,chOne 变得可用。 

           然后,我们再来看看 Struct,在struct 中每一个元素都是分配内存的,而且都是 
有单独意义的,也就是说对一个变量的赋值并不影响其它变量的取值。到这里,各位应该明 
白这两者之间的区别了吧,事实上我个人认为,它们最主要的区别是在内存的分配和使用上。 
知道这一点,一切也就不难理解了。 

           最后,在使用 union     的时候,可能有时候我们会来用其来对字节流进行分解和 
重组,这样使用的时候一定要注意各种内存对数据的存储,比如 Intel  是按高高低低的原则 
存储的,有些则是相反的。因此,这点因该值得注意,否则得到的可能和预期的结果不一样。 

   举例:使用union 结构输出主机字节序 

   int main ( void ) { union { short s; char c[sizeof(shor)]; }un; un.s = 0x0102; printf ( "%s:",
CPU_VENDOR_OS     );  if  (  2==sizeof(short)  )  { if  (  1==un.c[0]  &&  2==un.c[1])  {  printf
(   "big-endian\n"   );   }   else   if   (   2==un.c[0]   &&   1==un.c[1]   )   {   printf   (   "little-endian\n"   );   }   else
{ printf ( "unknown\n" ); } } else { printf ( "sizeof(short) = %d\n", sizeof(short) ); } return 0; }

   本   文    来   自     CSDN    博    客   ,    转   载    请   标   明    出   处    : 
http://blog.csdn.net/lvyexiaozi/archive/2008/07/07/2621694.aspx 



一、预备知识—程序的内存分配 
一个由c/C++编译的程序占用的内存分为以下几个部分 
1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。 
2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。 
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放 
4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放 
5、程序代码区—存放函数体的二进制代码。 

二、例子程序 
这是一个前辈写的,非常详细 
//main.cpp 
int a = 0; 全局初始化区 
char *p1; 全局未初始化区 
main() 

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


二、堆和栈的理论知识 

2.1申请方式 
stack: 
由系统自动分配。 例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间 
heap: 
需要程序员自己申请,并指明大小,在c中malloc函数 
如p1 = (char *)malloc(10); 
在C++中用new运算符 
如p2 = (char *)malloc(10); 
但是注意p1、p2本身是在栈中的。 

2.2 
申请后系统的响应 
栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。 
堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时, 
会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。 

2.3申请大小的限制 
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在 WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。 
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。 

2.4申请效率的比较: 
栈由系统自动分配,速度较快。但程序员是无法控制的。 
堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便. 

另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是直接在进程的地址空间中保留一快内存,虽然用起来最不方便。但是速度快,也最灵活 

2.5堆和栈中的存储内容 
栈: 在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。 
当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。 
堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。 
2.6存取效率的比较 
char s1[] = "aaaaaaaaaaaaaaa"; 
char *s2 = "bbbbbbbbbbbbbbbbb"; 
aaaaaaaaaaa是在运行时刻赋值的; 
而bbbbbbbbbbb是在编译时就确定的; 
但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。 
比如: 
#include 
void main() 

char a = 1; 
char c[] = "1234567890"; 
char *p ="1234567890"; 
a = c[1]; 
a = p[1]; 
return; 


对应的汇编代码 
10: a = c[1]; 
00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh] 
0040106A 88 4D FC mov byte ptr [ebp-4],cl 
11: a = p[1]; 
0040106D 8B 55 EC mov edx,dword ptr [ebp-14h] 
00401070 8A 42 01 mov al,byte ptr [edx+1] 
00401073 88 45 FC mov byte ptr [ebp-4],al 

第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指针值读到edx中,在根据edx读取字符,显然慢了。 

2.7小结: 
堆和栈的区别可以用如下的比喻来看出: 
使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。 
使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。 
堆和栈的区别主要分: 
操作系统方面的堆和栈,如上面说的那些,不多说了。 
还有就是数据结构方面的堆和栈,这些都是不同的概念。这里的堆实际上指的就是(满足堆性质的)优先队列的一种数据结构,第1个元素有最高的优先权;栈实际上就是满足先进后出的性质的数学或数据结构。 
虽然堆栈,堆栈的说法是连起来叫,但是他们还是有很大区别的,连着叫只是由于历史的原因。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 电信路由器坏了网线接口断了怎么办 数据线充手机的接头处断了怎么办 新买的小米手环充不进去电怎么办 绝地求生手游被队友故意炸死怎么办 一加3t屏幕开了怎么办? 孕妇吃了8个杏怎么办啊 洗碗盆落水器垫子密封不好怎么办? 手剥橙子剥的特别疼怎么办? 经常带对讲机的耳麦耳朵痛怎么办 公安检查遇到穿便装的军人怎么办 cf幽灵鬼呼吸辅军训刘海怎么办助 眼睛被等离子切割器的光烤了怎么办 玩王者的时候屏幕竖着了该怎么办 铝合金门双包门套比墙的厚了怎么办 磁共振检查后发现带金属了怎么办 贴了乳贴过免痒怎么办 yy别人听见我打游戏的声音怎么办 微信的聊天记录发错邮箱怎么办 百度云盘文件有违规内容怎么办 天籁一键启动钥匙没电怎么办 中兴手机系统界面已停止运行怎么办 怎么办可以复制成不关联的文档 希捷400g硬盘电机不转怎么办 金立手机微信语音播放失败怎么办 手机4g网络变2g怎么办 生存战争2吃了腐烂的食物后怎么办 古筝调音 d的显示为b怎么办 消防建审没有原有的结构图纸怎么办 生石灰弄到脸上用水洗后发热怎么办 吃了没熟的鹅肝怎么办 蹲式厕所被袜子堵了怎么办 自热包的水喝了怎么办 火锅发热包不小心吃了怎么办 塑料螺旋饭盒盖子被吸住了怎么办 昨晚喝太多酒今天排尿拍不出怎么办 开光过的貔貅摔坏一点嘴巴怎么办 诺基亚6第二代忘记解屏密码怎么办 工厂搬迁已经般空了工人怎么办 被上司强行换了一个岗位该怎么办 上司要调整我岗位我该怎么办 我被别人打伤了警察不管怎么办