Wie andere bereits angemerkt haben, ist dies nur möglich, wenn (1) Ihr Compiler die Tail Call-Optimierung unterstützt und (2) Ihre Funktion für eine solche Optimierung geeignet ist. Die Optimierung besteht darin, den vorhandenen Stapel wiederzuverwenden und ein JMP (d. H. Ein GOTO in Assembly) anstelle eines CALLs auszuführen.
Tatsächlich ist Ihre Beispielfunktion tatsächlich für eine solche Optimierung geeignet. Der Grund dafür ist, dass das Letzte, was Ihre Funktion vor der Rückkehr macht, sich selbst anrufen soll; es muss nichts tun nach der letzte Anruf an funcnew()
. Allerdings werden nur bestimmte Compiler eine solche Optimierung durchführen. GCC zum Beispiel wird es tun. Weitere Informationen finden Sie unter Which, if any, C++ compilers do tail-recursion optimization?
Das klassische Material dazu ist die faktorielle Funktion. Lassen Sie uns eine rekursive faktorielle Funktion machen, die nicht für Tail-Call-Optimierung (TCO) geeignet ist.
int fact(int n)
{
if (n == 1) return 1;
return n*fact(n-1);
}
Das letzte, was sie tut, ist n
mit dem Ergebnis aus fact(n-1)
zu multiplizieren. Indem wir diese letzte Operation irgendwie eliminieren, könnten wir den Stapel wiederverwenden. Lassen Sie uns einen Akkumulator Variable einzuführen, die Antwort wird für uns berechnen:
int fact_helper(int n, int acc)
{
if (n == 1) return acc;
return fact_helper(n-1, n*acc);
}
int fact_acc(int n)
{
return fact_helper(n, 1);
}
Die Funktion fact_helper
die Arbeit macht, während fact_acc
nur eine Komfortfunktion ist der Akkumulator Variable zu initialisieren.
Beachten Sie, wie die letzte Sache fact_helper
ist, sich selbst zu nennen. Dieser CALL kann in einen JMP konvertiert werden, indem der vorhandene Stapel für die Variablen wiederverwendet wird.
Mit GCC, können Sie überprüfen, ob es an der generierten Assembly, indem man auf einen Sprung optimiert, zum Beispiel :
...
37 L12:
38 0040 0FAFC2 imull %edx, %eax
39 0043 83EA01 subl $1, %edx
40 0046 83FA01 cmpl $1, %edx
41 0049 75F5 jne L12
...
Einige Programmiersprachen wie Scheme wird, dass tatsächlich garantieren, richtige Implementierungen führe solche Optimierungen durch. Sie werden es sogar für nicht-rekursive Tail-Calls tun.
Ich sehe "Funktion, die sich aufruft" in Ihrem Codebeispiel nicht. – AnT
'statisch' ist dein Freund? (immer noch ohne Ende Bedingung stapeln Sie Überlauf mit Rückkehrzeiger). –