Die Community zu .NET und Classic VB.
Menü

FAQ 0077: Laufzeitfehler 28 - Was ist das und wie vermeide ich ihn?

 von 

Frage 

Während der Ausführung meiner Anwendung erhalte ich den die Meldung 'Laufzeitfehler 28: Nicht genügend Stapelspeicher'.
Was ist das und wie vermeide ich diesen Fehler?

Antwort  

Dieser Fehler tritt oft im Zusammenhang mit Rekursion auf.

Zuerst eine kleine Erklärung:
Der Stapelspeicher verwaltet lokale Prozedurvariablen. Beim Aufruf einer Prozedur werden alle lokalen Variablen auf dem sog. Stapelspeicher (engl: "Stack") abgelegt. Auf diese Weise muß sich der Programmierer nicht selbst umständlich um das Anlegen der Variablen kümmern (für VB irrelevant - nicht so für andere Sprachen).

Dieses Konzept nun macht sich die Rekursion zunutze: Es handelt sich um eine Programmiertechnik, bei der sich eine Prozedur selbst aufruft.
Rekursion kann man zum Beispiel verwenden, um die Fakultät einer Zahl zu errechnen (zur Erinnerung: die Fakultät einer Zahl n, notiert n!, ist das Produkt aus n und all seinen Vorgängern, also n! = 1 * 2 * 3 * ... * n).
Das geht ganz einfach:

Function Fak(Zahl As Long) As Long
    If Zahl = 1 Then
        Fak = 1
    Else
        Fak = Zahl * Fak(Zahl - 1)
    End If
End Function

Listing 1: Funktion mit offener Rekursion

Diese Funktion illustriert auch gleich das Problem der Rekursion: Per Definition ist 0! (Fakultät von Null) = 1.
Allerdings ist das im obigen Aufruf nicht ersichtlich: das Programm erkennt lediglich, daß Zahl ungleich 1 ist und ruft sich daher erneut auf - mit dem Parameter (Zahl - 1).
Das heißt: Es entsteht eine Schleife, die nicht abgebrochen wird, eine sogenannte Endlosschleife.
Da auch die Parameter einer Funktion bei deren Aufruf auf dem Stack abgelegt werden, wächst dieser nun ständig. Dadurch kommt der Laufzeitfehler 28 zustande.

Das heißt generell: Wenn der Fehler 28 auftritt, liegt irgendwo eine unendliche Rekursion vor.

Man sollte daher als ersten Schritt alle Funktionen auf gültige Abbruchbedingungen überprüfen und sicherstellen, daß selbst im Falle einer Fehleingabe irgendwann die Rekursion verlassen wird.

Ein anderer Typ der Rekursion ist leider nicht auf den ersten Blick ersichtlich und führt daher oft zu Konfusion: Rekursion über einen dritten Befehl.
Gegeben sei zum Beispiel folgender Code (original aus unserem Forum):

Private Sub Text1_KeyPress(KeyAscii As Integer)
    Select Case KeyAscii
        Case Asc("0") To Asc("9")
            Exit Sub
        Case Asc(",")
            Exit Sub
        Case Else
            Call SendKeys("{BACKSPACE}" & "{DELETE}", Wait:=True)
    End Select
End Sub

Listing 2: Funktion mit versteckter Rekursion

Der Code soll verhindern, daß der Benutzer in ein Textfeld etwas anderes als Zahlen beziehungsweise ein Dezimaltrennzeichen eingibt. Bei jeder anderen Eingabe wird das Zeichen per Aufruf von SendKeys() gelöscht.
Hier nun tritt eine unendliche Rekursion auf, da ein SendKeys() das Ereignis KeyPress auslöst, wodurch die obenstehende Sub erneut aufgerufen wird, und erneut, und erneut ...

Übrigens sollte man Rekursionen, wenn möglich vermeiden.
Zwar ist die Technik sehr interessant, aber in der Praxis oft sehr viel resourcenaufwendiger als eine äquivalente induktive Methode.
So läßt sich die Funktion "Fak" von oben umschreiben:

Function Fak(Zahl As Long) As Long
    Dim I As Long

    Fak = 1

    For I = 2 To Zahl
        Fak = Fak * I
    Next I
End Function

Listing 3: Funktion ohne Rekursion

Zwar sieht man es der Funktion ansich gar nicht an, aber sie ist rund dreimal schneller als die rekursive Methode!

Merke: jede rekursive Methode kann durch ein induktives Äquivalent ersetzt werden. Nur in Fällen, in denen der induktive Code sehr viel länger ist, sollte man auf diesen Ausweg verzichten.

Ihre Meinung  

Falls Sie Fragen zu dieser FAQ haben, Ihre Erfahrung mit anderen Nutzern austauschen möchten oder auf eine Ergänzung hinweisen möchten, dann teilen Sie uns diese bitte in einem der unten vorhandenen Themen oder über einen neuen Beitrag mit. Hierzu können sie einfach einen Beitrag in einem zum Thema passenden Forum anlegen, welcher automatisch mit dieser Seite verknüpft wird.