2012-08-27 108 views
18

在C++中,堆棧溢出通常會導致程序無法恢復崩潰。對於需要非常健壯的程序,這是一個不可接受的行爲,特別是因爲堆棧大小有限。關於如何處理問題的幾個問題。如何處理或避免C++中的堆棧溢出

  1. 有沒有一種方法可以通過一般技術來防止堆棧溢出。 (可擴展的,可靠的解決方案,包括應對外部庫吃了很多棧等)

  2. 有沒有辦法來處理他們的情況下發生堆棧溢出?優選地,堆棧被解開,直到有處理者處理那種問題。

  3. 有那裏的語言,具有可擴展堆棧線程。在C++中有這樣的可能嗎?

在C++行爲的溶液中的任何其它有用的意見,將不勝感激。

+4

http://support.microsoft.com/kb/315937 –

+1

該標準甚至沒有提到堆棧,您應該指定您定位的平臺;幾個平臺提供了攔截堆棧溢出的方法,甚至當堆棧差不多用完時,甚至會發出「堆棧溢出警報」。 –

+5

就我個人而言,我發現SO不是要避免的,而是要擁抱。只要看看這裏的偉大社區! –

回答

17

處理堆棧溢出是不是正確的解決方案,相反,你必須確保你的程序不使棧溢出。

不要在堆棧上分配大的變量(其中「大」取決於程序)。確保任何遞歸算法在已知最大深度後終止。如果遞歸算法可能遞歸未知次數或大量次數,則自己管理遞歸(通過維護您自己的動態分配堆棧)或將遞歸算法轉換爲等效迭代算法

「非常健壯」不會使用第三方或外部庫「吃了很多堆棧」。


注意,有些平臺時發生堆棧溢出通知程序,並允許程序來處理錯誤。例如,在Windows上,拋出異常。這個異常不是C++異常,但它是一個異步異常。鑑於C++異常只能由throw語句拋出,在執行程序的任何時候都可能會引發異步異常。不過,這是預料之中的,因爲堆棧溢出可能隨時發生:任何函數調用或堆棧分配都可能使堆棧溢出。

問題是堆棧溢出可能會導致即使代碼不會引發任何異常(例如,在C++中標記爲noexceptthrow()的函數)也會拋出異步異常。所以,即使你以某種方式處理這個異常,你也無法知道你的程序處於安全狀態。因此,處理異步異常的最好方法是不要處理它,根本不是(*)。如果拋出一個,這意味着該程序包含一個錯誤。

其他平臺可能有「處理」一個堆棧溢出錯誤類似的方法,但是任何這樣的方法可能來自同一個問題遭受:,預計不會導致錯誤可能會導致錯誤代碼。

(*)有一些非常罕見的例外。

+0

使用第三方庫通常是一個成本問題。如果您在某個時間點在您的程序的Windows上,則必須直接或間接使用WinApi。在其他系統上一樣。你將不能沒有第三方庫。 (你可能會認爲C++標準庫是第三方。)但是我的觀點是,如果你想要一個超級健壯的程序並使用第三方庫來降低成本,那麼你可能需要一種方法來保證程序不會其他庫中的堆棧溢出完全崩潰。 –

0

C++是一種強大的語言,並與力量來自於搬起石頭砸自己的腳的能力。我不知道有任何便攜式機制在發生堆棧溢出時檢測並糾正/中止。當然,任何這種檢測都是針對特定實現的。例如g ++提供-fstack-protector來幫助監控你的堆棧使用情況。

總的來說,最好的選擇是積極主動地避免大型的基於堆棧的變量,並且謹慎使用遞歸調用。

+0

'-fstack-protector'不能幫助監控多餘的堆棧分配。它用於檢測堆棧分配的變量寫入超出邊界的時間。 –

3

可以防止堆棧溢出使用良好的編程習慣,如:

  1. 要非常小心使用遞歸,我最近看到一個SO從寫的不好遞歸CreateDirectory功能造成,如果你要是不知道你的代碼是100%ok,然後添加防護變量,在N次遞歸調用後停止執行。或者甚至更好的不寫遞歸函數。
  2. 不要在堆棧上創建巨大的數組,這可能會隱藏數組,像一個非常大的數組作爲類字段。它總是更好地使用矢量。
  3. 要非常小心alloca,特別是如果它被放入一些宏定義。我已經看到了許多因字符串轉換宏而產生的SO,這些宏被放入使用alloca進行快速內存分配的for循環中。
  4. 確保您的堆棧大小最佳,這在嵌入式平臺中更爲重要。如果你的線程沒有做太多的話,那麼給它一小堆,否則使用較大。我知道保留應該只採取一些地址範圍 - 而不是物理內存。

這些是我在過去幾年中見過的最主要的原因。

對於自動找到你應該能夠找到一些靜態代碼分析工具。

+0

感謝這些建設性的想法。 –

0

Re:可擴展堆棧。你可以給自己更多的堆棧空間,像這樣:

#include <iostream> 

int main() 
{ 
    int sp=0; 

    // you probably want this a lot larger 
    int *mystack = new int[64*1024]; 
    int *top = (mystack + 64*1024); 

    // Save SP and set SP to our newly created 
    // stack frame 
    __asm__ ( 
     "mov %%esp,%%eax; mov %%ebx,%%esp": 
     "=a"(sp) 
     :"b"(top) 
     : 
     ); 
    std::cout << "sp=" << sp << std::endl; 

    // call bad code here 

    // restore old SP so we can return to OS 
    __asm__(
     "mov %%eax,%%esp": 
     : 
     "a"(sp) 
     :); 

    std::cout << "Done." << std::endl; 

    delete [] mystack; 
    return 0; 
} 

這是gcc的彙編語法。

+0

哎呀,使'int * top =(mystack + 64 * 1024 - 1);' –

+0

也許還有足夠的力量在腳下自己射擊。 –

+0

哎唷!真是個黑客! –

-2
#include <iostream> 
using **namespace** std; 
class Complex 
{ 

public: double *re, *im; 

Complex() 
{ 

    re = new double(r); 

    im = new double(m); 

} 

Complex() 
{ 

    re = new double; 
    im = new double; 
    *re = *t.re; 
    *im= *t.im; 

} 

~Complex() 
{ 

     delete re, im; 

} 

}; 

int main() { 

double x, y, z; 
cin >> x >> y >> z; 
Complex n1(x,y); 
cout << *n1.re << "+" << *n1.im << "i "; 
Complex n2 = n1; 
cout << *n2.re << "+" << *n2.im << "i "; 
*n1.im = z; 
cout << *n2.re << "+" << *n2.im << "i "; 
cout << *n1.re << "+" << *n1.im << "i "; 
return 0; 
} 
+1

這與這個問題有什麼關係? –