當(dāng)前位置:首頁 > 嵌入式培訓(xùn) > 嵌入式學(xué)習(xí) > 講師博文 > 堆棧溢出一般是什么原因?
堆棧是一個在計算機科學(xué)中經(jīng)常使用的抽象數(shù)據(jù)類型。堆棧中的物體具有一個特性: 最后一個放入堆棧中的物體總是被最先拿出來, 這個特性通常稱為后進(jìn)先出(LIFO)隊列。 堆棧中定義了一些操作。 兩個最重要的是PUSH和POP。 PUSH操作在堆棧的頂部加入一 個元素。POP操作相反, 在堆棧頂部移去一個元素, 并將堆棧的大小減一。
堆棧溢出的產(chǎn)生是由于過多的函數(shù)調(diào)用,導(dǎo)致調(diào)用堆棧無法容納這些調(diào)用的返回地址,一般在遞歸中產(chǎn)生。堆棧溢出很可能由無限遞歸(Infinite recursion)產(chǎn)生,但也可能僅僅是過多的堆棧層級。
一般產(chǎn)生溢出的原因如下:
1.函數(shù)調(diào)用層次太深。函數(shù)遞歸調(diào)用時,系統(tǒng)要在棧中不斷保存函數(shù)調(diào)用時的現(xiàn)場和產(chǎn)生的變量,如果遞歸調(diào)用太深,就會造成棧溢出,這時遞歸無法返回。再有,當(dāng)函數(shù)調(diào)用層次過深時也可能導(dǎo)致棧無法容納這些調(diào)用的返回地址而造成棧溢出。
2.動態(tài)申請空間使用之后沒有釋放。由于C語言中沒有垃圾資源自動回收機制,因此,需要程序主動釋放已經(jīng)不再使用的動態(tài)地址空間。申請的動態(tài)空間使用的是堆空間,動態(tài)空間使用不會造成堆溢出。
3.數(shù)組訪問越界。C語言沒有提供數(shù)組下標(biāo)越界檢查,如果在程序中出現(xiàn)數(shù)組下標(biāo)訪問超出數(shù)組范圍,在運行過程中可能會內(nèi)存訪問錯誤。
4.指針非法訪問。指針保存了一個非法的地址,通過這樣的指針訪問所指向的地址時會產(chǎn)生內(nèi)存訪問錯誤。
堆溢出:不斷的new 一個對象,一直創(chuàng)建新的對象,
棧溢出:死循環(huán)或者是遞歸太深,遞歸的原因,可能太大,也可能沒有終止。
通常「堆棧溢出」是指「調(diào)用堆棧(call stack)的溢出」。要通俗地解釋調(diào)用堆棧可能比較困難,因為它涉及許多其他計算機架構(gòu)的知識。而這個答案只是簡單地解釋堆棧這種數(shù)據(jù)結(jié)構(gòu)的特點──先進(jìn)后出/后進(jìn)先出。溢出是指這個數(shù)據(jù)結(jié)構(gòu)滿溢,不能存放更多數(shù)據(jù)。其他的數(shù)據(jù)結(jié)構(gòu)也會遇到這個情況。即使數(shù)據(jù)結(jié)構(gòu)并非固定容量,而是可擴(kuò)展的,在有限的內(nèi)存空間下仍是有滿溢的機會。
另外,很多時候,「調(diào)用堆棧溢出」的出現(xiàn)是與遞歸(recursion)相關(guān)的。我們可以把一些遞歸的實現(xiàn)改為迭代(iteration),但有時還是必須有一個自定義的堆棧數(shù)據(jù)結(jié)構(gòu),例如對樹的深度優(yōu)先搜索(Depth-First Search, DFS)。自定義的堆棧也是有溢出的可能。
所以,雖然堆棧溢出常指調(diào)用堆棧溢出,但本質(zhì)上也只是一種數(shù)據(jù)結(jié)構(gòu)的滿溢情況