|
Teil 1
Was auf den ersten Blick etwas kompliziert erscheint ist eigentlich halb so wild. Wir wollen das Programm jetzt Schritt für Schritt. Wir gehen jetzt davon aus wir übergeben der Funktion die Werte x=8, und y=2 also rufen wir die Funktion mittels....
...auf. Als 1. Überprüfen wir das wichtigste in unserer Funktion - die Abbruchbedienung.....
Da x=8 und y=2 und die if - Bedienung war ist springen in die nächste Zeile...
Wir geben an unsere Funktion divide den Wert 1 zurück plus den Wert der Funktion divide(x-y,y) . Hoppla werden sie jetzt sagen, richtig erkannt wir rufen hiermit die Funktion divide(int x,int y) erneut auf. Hiermit beginnt die Rekursion. Aber was passiert jetzt eigentlich mit dem Rückgabewert 1? Schauen wir uns das ganze zum besseren Verständnis mal bildlich an...
Auf dem Stack wurde zuerst die main - Funktion gelegt, da diese als erstes die Funktion divide aufgerufen hat. Und irgendwo muss ja gespeichert sein wie Ihr Programm wieder zur main - Funktion zurückkommt. Man kann sich das etwa so vorstellen. Bei jedem Funktionsaufruf, egal ob jetzt Rekursiv oder nicht, wird der Aktuelle Zustand der main- Funktion eingefroren und auf dem Stack abgelegt. Damit das Programm aber wieder weis wo die Adresse der main - Funktion wahr wir auf dem Stack immer auch eine Rücksprungadresse mitabgelegt. Ich mache mit der Programmausführung weiter bevor ich sie total verwirre.
...also divide(8-2,2) da x=8 und y=2. Jetzt überprüfen wir erneut die Anweisung....
Da nun x=6 und y=2 und somit unsere if - Abfrage wieder wahr ist geht die Programmausführung wieder in der nächsten Zeile weiter. Und es geht erneut wieder mit einem Selbstaufruf der Funktion divide() weiter.....
....also wird wieder auf unserem Stack abgelegt....
Somit liegen auf unserem Stack 2x der Wert 1 inklusive Rücksprungadresse (die ist hier nicht mitabgebildet). Nun wiederholt sich das ganze Spiel noch zweimal bis es auf unserem Stack folgendermaßen aussieht...
Nun wird erneut die Funktion divide() aufgerufen mit den Werten...
Jetzt endlich wird unsere Abbruchbedienung...
...aktiv. Denn nun ist x=0 und y=2 und somit wir die Programmausführung nicht mehr in der nächsten Zeile ausgeführt. Die nächste Abfrage...
...dient nur dazu, falls x ungleich 0 sein sollte, den Rest auszugeben. In unserem Beispiel gibt es keinen Rest. Nun wird an unseren der Wert 0 (return 0) zurückgegeben. Jetzt muss natürlich unser Programm schauen nach dem ein Funktion beendet wurde wo die nächste Rücksprungadresse ist. Schauen wir nochmals unseren Stack an....
Der Rückgabewert 0 wurde von dem Funktionsaufruf divide(2-2,2) erzeugt. Dorthin führt auch die Rücksprungadresse. Also return 0+1. Die nächste Rücksprungadresse wurde von divide(4-2,2) erzeugt also folgt return 0+1+1. Anschließend folgt return 0+1+1+1 und als letztes return 0+1+1+1+1. Somit könnten wir unseren Stack folgendermaßen aussehen....
Jetzt bekommt unsere main - Funktion den Rückgabewert 0+1+1+1+1 also 4 und richtig 8/2 ist auch 4.
Dies Programm erfüllt doch den selben Zweck und ist einfacher. Und sie haben Recht das rekursive Programm ist zum einen schwieriger und zum anderen langsamer, da ständig etwas auf dem Stack gepusht und wieder gepopt werden muss. Und das kann dauern. Mit einem Wort die rekursive Lösung ist die schlechtere Lösung in diesem Beispiel. Schlimmer noch die rekursive Lösung verbraucht viel Speicherplatz zum Anlegen von Parametern, lokale Variablen, Rückgabewerte und Rücksprungadressen. Nehmen wir mal sie wollen die Zahl 1.000.000 durch 2 teilen. Für unsere 2 Parameter x und y benötigen wir schon mal 8 Byte pro Aufruf. Für den Rückgabewert (return 1) benötigen wir 4 Bytes genauso wie für die Rücksprungadresse. Das heißt wir benötigen für eine Ablage auf dem Stack 16 Byte. Nun wenn wir die Zahl 1.000.000 durch 2 teilen bedeutet dies das auf unserem Stack 500.000 Werte ala 16 Bytes liegen. Das sind ca.7,6MB Speicher die wir durch einen Rekursive Aufruf eines solch einfachen Problem verschwenden. |