1. 程式人生 > >棧溢位(stack overflow)的原因及解決辦法

棧溢位(stack overflow)的原因及解決辦法

最近在做一個程式(VC6.0),功能大概有網路通訊、資料庫、繪圖等。測試的時候程式一執行到某個函式就出現此錯誤,查了很多地方,試了很多解決辦法,終於把問題解決了,寫個日誌提醒一下自己,也希望作為一個普遍解決辦法讓大家少費工夫(其他編譯器也會出現同樣的問題)。
   大家都知道,Windows程式的記憶體機制大概是這樣的,全域性變數(區域性的靜態變數本質也屬於此範圍)儲存於堆記憶體,該段記憶體較大,一般不會溢位;函式地址、函式引數、區域性變數等資訊儲存於棧記憶體,VC6中棧記憶體預設大小為1M,對於當前日益擴大的程式規模而言,稍有不慎就可能出問題。
(動態申請的記憶體即new出來的記憶體不在棧中)
   即如果函式這樣寫:
   void test_stack_overflow()
   {
      char* chdata = new[2*1024*1024];
      delete []chdata;
   }
   是不會出現這個錯誤的,而這樣寫則不行:
   void test_stack_overflow()
   {
      char chdata[2*1024*1024];
   }
   大多數情況下都會出現記憶體溢位的錯誤,不信在vc6中隨便做個程式,呼叫一下這個函式試式。 


   出現棧記憶體溢位的常見原因有2個:
   1> 函式呼叫層次過深,每呼叫一次,函式的引數、區域性變數等資訊就壓一次棧。
   2> 區域性靜態變數體積太大
   第一種情況不太常見,因為很多情況下我們都用其他方法來代替遞迴呼叫(反正我是這麼做的),所以只要不出現無限制的呼叫都應該是沒有問題的,起碼深度幾十層我想是沒問題的,這個我沒試過但我想沒有誰會把呼叫深度作那麼多。檢查是否是此原因的方法為,在引起溢位的那個函式處設一個斷點,然後執行程式使其停在斷點處, 然後按下快捷鍵Alt+7調出call stack視窗,在視窗中可以看到函式呼叫的層次關係。

   第二種情況比較常見了,我就是犯了這個錯誤,我在函式裡定義了一個區域性變數,是一個類物件,該類中有一個大陣列,大概是1.5M。
    
    解決辦法大致說來也有兩種:
   1> 增加棧記憶體的數目
   2> 使用堆記憶體
   增加棧記憶體方法如下,在vc6種依次選擇Project->Setting->Link,在Category中選擇output,在Reserve中輸入16進位制的棧記憶體大小如:0x10000000,然後點ok就可以了。
  
   其他編譯器也有類似的設定,個人認為這不是一個好辦法,有一個致命原因,不知道有沒有人遇到過,我把棧記憶體改大後,與資料庫建立不了連線了(ADO方式,Acess資料庫),把棧記憶體還原,問題立刻消失。不知道究竟是什麼原因,有知道的可以告訴我。
    email: @sina.com

    第二種解決辦法是比較可行的,具體實現由很多種方法可以直接把陣列定義改成指標,然後動態申請記憶體;也可以把區域性變數變成全域性變數,一個偷懶的辦法是直接在定義前邊加個static,呵呵,直接變成靜態變數(實質就是全域性變數)。即可以把上例中的函式這麼寫:

   void test_stack_overflow()
   {
      static char chdata[2*1024*1024];
   }


   當然,除非萬不得已,儘量不要使用這麼大的陣列,出現這種情況多半說明程式結構有問題。