1. 程式人生 > >C++:堆與棧區別

C++:堆與棧區別

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個元素有最高的優先權;棧實際上就是滿足先進後出的性質的數學或資料結構。
雖然堆疊,堆疊的說法是連起來叫,但是他們還是有很大區別的,連著叫只是由於歷史的原因。