簡體   English   中英

C ++中最大遞歸調用次數的數量級是多少?

[英]What is the order of magnitude of the maximum number of recursive calls in C++?

我有一個遞歸函數,在給定某些輸入的情況下會調用很多次 - 這正是應該做的。 我知道我的函數不是無限循環 - 它只是達到一定數量的調用和溢出。 我想知道在堆棧上放置太多內存是否存在問題,或者只是調用數量的正常限制。 顯然很難說出一個特定數量的呼叫是最大的,但任何人都能給我一個粗略估計的數量級嗎? 它是成千上萬? 數百? 百萬?

所以,正如您所猜測的那樣,問題是(同名)堆棧溢出。 每次調用都需要設置一個新的堆棧幀,將新信息壓入堆棧; 堆棧大小是固定的,並最終耗盡。

什么設置堆棧大小? 這是編譯器的屬性 - 也就是說,它是針對二進制可執行文件修復的。 在Microsoft的編譯器(在VS2010中使用)中,它默認為1兆字節,您可以在編譯器選項中使用“/ F”覆蓋它(有關'03示例,請參閱此處 ,但語法相同)。

要弄清楚在實踐中等同於多少次通話是非常困難的。 函數的堆棧大小由它的局部變量,返回地址的大小以及參數的傳遞方式(有些可能在堆棧中)決定,其中大部分也取決於體系結構。 通常,您可以假設后兩者小於一百字節(這是一個粗略估計)。 前者取決於你在函數中做了什么。 如果你假設函數在堆棧上占用了256個字節,那么在1M堆棧中你會在溢出之前得到4096個函數調用 - 但這並沒有考慮到main函數的開銷等。

您可以嘗試減少局部變量和參數開銷,但真正的解決方案是Tail Call Optimization ,其中編譯器在調用遞歸函數時釋放調用函數。 您可以在此處閱讀有關在MSVC中執行此操作的更多信息。 如果您無法進行尾調用,並且無法以可接受的方式減小堆棧大小,那么您可以使用“/ F”參數查看增加的堆棧大小,或者(首選解決方案)查看重新設計。

它完全取決於您在堆棧上使用的信息量。 但是,Windows上的默認堆棧為1MB,Unix上的默認堆棧為8MB。 簡單地撥打一個電話可能需要推送一些32位寄存器和一個返回地址,這樣你可能會看到一個可能20字節的調用,這將使Windows上的最大值為50k,而Unix上的最大值為400k,這是一個空函數。

當然,據我所知,你可以改變堆棧大小。

一個選項可能是更改/增加默認堆棧大小。 這是一種方式http://msdn.microsoft.com/en-us/library/tdkhxaks(v=vs.80).aspx

有一些工具可以衡量堆棧的使用情況。 它們使用特定的字節模式預先填充堆棧,然后查看它更改的地址。 有了這些,你可以找到你有多接近極限。

也許其中一個valgrind工具可以做到這一點。

遞歸函數使用的堆棧空間量取決於遞歸的深度和每次調用使用的內存空間量。

遞歸的深度是指在任何給定時刻激活的呼叫級別的數量。 例如,二叉樹可能有一百萬個節點,但如果它很平衡,你可以在不超過20個同時活動的調用的情況下遍歷它。 如果它沒有很好地平衡,最大深度可能會更大。

每次調用使用的內存量取決於遞歸函數中聲明的變量的總大小。

最大遞歸深度沒有固定限制; 如果您的總使用量超過系統強加的堆棧限制,您將獲得堆棧溢出。

您可以通過某種方式減少遞歸的深度來減少內存使用量,可能通過重構您正在遍歷的任何內容(您沒有告訴我們太多),或者通過減少聲明的任何本地對象的總大小在遞歸函數內部(請注意,堆分配的對象不會對堆棧大小有所貢獻),或兩者的某種組合。

正如其他人所說,你可能會增加你允許的堆棧大小,但這可能只是有限的使用 - 而且在運行你的程序之前你需要做的另外一件事。 它還可能消耗資源並干擾系統上的其他進程(由於某種原因而施加限制)。

更改算法以避免遞歸可能是可能的,但同樣,我們沒有足夠的信息來說明這一點。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM