2010-04-13 23 views
41

In Python gibt es eine maximale Rekursionstiefe. Es scheint, weil Python ein Interpreter ist, kein Compiler. Hat C++ das gleiche Konzept? Oder ist es nur mit RAM-Limit verbunden?Begrenzt C++ die Rekursionstiefe?

+2

Ich nehme an, Sie meinen eine * maximale * Rekursionstiefe. (Wie tief die Rekursion sein darf, bevor ein Fehler auftritt) – jalf

+1

Ich sehe nicht, warum ein Interpreter/Compiler damit etwas zu tun haben würde. Zum Beispiel, Stackless Python ist immer noch ein Interpreter, aber verwendet nicht den C-Stack für seine Stack-Frames und hat daher nicht das gleiche Problem, nein? – Ken

+0

Beachten Sie, dass es C++ - und Python-Implementierungen gibt, die Tail-Call-Eliminierungen durchführen können, um in diesen Fällen das Erreichen von Stack-Limits zu vermeiden. –

Antwort

39

Das Limit in C++ ist aufgrund der maximalen Größe des Stapels. Das ist in der Regel weniger als die Größe des RAM um einige Größenordnungen, aber immer noch ziemlich groß. (Glücklicherweise werden große Dinge wie die Zeichenfolge Inhalt normalerweise nicht auf dem Stapel selbst gehalten.)

Das Stapellimit ist normalerweise auf Betriebssystemebene einstellbar. (Siehe die Dokumentation für die Shell ulimit, wenn Sie mit Unix arbeiten.) Der Standardwert für dieses System (OSX) ist 8 MB.

[EDIT] Natürlich hilft die Größe des Stacks nicht ganz von selbst, wenn es darum geht, herauszufinden, wie tief Sie rekrutieren können. Um das zu wissen, müssen Sie die Größe des Aktivierungseintrags (oder Datensätze) der rekursiven Funktion (auch Stack-Frame genannt) berechnen. Der einfachste Weg dies zu tun (den ich kenne) ist, einen Disassembler (eine Funktion der meisten Debugger) zu verwenden und die Größe der Stack-Pointer-Anpassungen am Anfang und am Ende jeder Funktion auszulesen. Was ist chaotisch. (Sie können es auf andere Weise - zum Beispiel die Differenz zwischen Zeigern zu Variablen in zwei Aufrufen berechnen - aber sie sind noch fies, vor allem für tragbaren Code. Lesen der Werte aus der Demontage ist einfacher IMO.)

+2

Ich weiß, ich hätte MiB für die Einheiten sagen sollen, aber das bedeutet etwas ganz anderes für mich. Also werde ich stattdessen für 67.1 Megabit gehen! –

+0

Auf Windows-Rechnern können Sie A. die Bedingungen für den Stack-Überlauf sicher handhaben und B. die Stack-Tiefe für jede ausführbare Datei individuell festlegen (es handelt sich um eine Linker-Option). Unix-Computer verwenden aufgrund dieser Funktionen normalerweise wesentlich tiefere Stapel (8 MB) als Windows (1 MB). +1 –

+0

Ich denke, dass es auch in diesem Bereich Unterschiede gibt, wie die beiden OS-Familien mit virtuellem Speicher umgehen. Die Details werden eher nicht trivial. –

3

C++ hat eine maximale Rekursionstiefe, begrenzt durch den Stack. Moderne Betriebssysteme sind jedoch in der Lage, einen Userspace-Stack dynamisch zu erweitern, wenn er sich füllt, wodurch die Rekursionstiefe nur durch Speicherplatz und Speicherfragmentierung begrenzt wird.

+1

Hmm .. Ich bin nicht ganz positiv Unixen bieten diese Funktion. Aber ich könnte sehr sehr falsch liegen :) Es scheint, dass 'boost :: regex' solche Features verwenden würde, wenn sie verfügbar wären ... es nutzt sie unter Windows. –

+0

Dynamische Stack-Erweiterung klingt interessant. Hast du eine Referenz? –

+0

@Don Wakefield: http://support.microsoft.com/kb/315937 –

3

Ich glaube, das Limit ist die Größe des Stacks auf der Plattform. Von dem, was ich gelesen habe, ist es 8K 8MB standardmäßig auf Linux, aber moderne Kernel können die Stapelgröße dynamisch anpassen.

+2

8K ist ziemlich flach für einen Stapel. Ich dachte, es bedeutet, dass es bei 8K beginnt und um Seiten erweitert. –

+0

Eigentlich ist es 8M standardmäßig :) –

+0

Eigentlich könnte 8MB sein - der Artikel, den ich gelesen habe, war nicht klar. –

6

Es gibt keine Rekursionstiefennachverfolgung oder -grenze in den C- oder C++ - Standards. Zur Laufzeit ist die Tiefe davon abhängig, wie groß der Stack wachsen kann.

+0

Tatsächlich ist die Beschränkung eine Kombination aus der Adressierungsfähigkeit der Plattform, der Menge an Speicher, die für den Prozess verfügbar ist, und irgendwelchen Einschränkungen, die durch den Compiler gesetzt werden. Im Allgemeinen können Anwendungen den Stapel überschreiben und nicht definiertes Verhalten ausführen. –

+0

Außerdem werden alle Laufzeitbeschränkungen im 'ulimit'-Stil oder im Fall von Threads alle während der Erstellung von Threads gesetzten Grenzen gesetzt. Ich bin mir nicht sicher, ob Sie mit "Override the stack" "auf eine Größe setzen, die das Betriebssystem nicht unterstützen kann (zB unbegrenzt), weil ein anderes Limit zuerst erreicht wird" oder "viel zu viel auf den Stack schieben und überrannt das Ende."Es gibt nichts Besseres als 256 KB an automatischem Speicher in Ihrer rekursiven Funktion, das Überschießen einer unzureichenden Anzahl von schreibgeschützten Stapelschutzseiten am Ende und das Erzielen eines Segmentierungsfehlers oder einer Heap-Beschädigung anstelle eines Nur-lesen-Nur-Seite-Fehlers ... –

19

Nein, C++ hat keine explizite Rekursionstiefe. Wenn die maximale Stackgröße überschritten wird (standardmäßig 1 MB unter Windows), wird Ihr Stack von Ihrem C++ - Programm überlaufen und die Ausführung wird beendet.

+3

Yep. +1. Außerdem ist es wichtig zu beachten, dass die Beendigung sofort ist - ähnlich wie beim Aufruf von TerminateProcess. Keine der 'nice-shutdown-Funktionen Ihres Prozesses (dh DLL_PROCESS_DETACH, DLL_THREAD_DETACH, usw.) wird aufgerufen. –

+3

Dies ist eine der unangenehmeren Möglichkeiten, ein Programm zu beenden. –

3

Python hat eine tunable limit auf rekursive Aufrufe, während C++ durch die Stapelgröße begrenzt ist.

Darüber hinaus können viele Sprachen oder Compiler die Tail-Rekursion optimieren, indem sie den Stack-Frame des Aufrufers entfernen, so dass kein zusätzlicher Stack-Speicherplatz verbraucht wird. (In Endrekursion, das einzige, was die aufrufende Funktion tut, ist nach der rekursive Aufruf machen, ist der rekursive Aufruf Rückgabewert zurückzukehren.)

int fact(int n, int accum=1){ 
    if (n==0) return accum; 
    else return fact(n-1,n*accum); //tail recursion here. 
} 

Python nicht optimiert Endrekursion (aber stackless Python der Fall ist), und C++ tut nicht Tail-Rekursion-Optimierung, aber ich glaube, dass gcc die Tail-Rekursion optimiert. Die JVM optimiert die Tail-Rekursion nicht, obwohl die Scala-Sprache dies in bestimmten häufig dokumentierten Fällen tut. Scheme und Lisp (und wahrscheinlich auch andere funktionale Sprachen) erfordern, dass die Tail-Rekursion optimiert wird.

Verwandte Themen