Zeiger in VB - Seite 3
von Konrad Rudolph
Das Heap-Modul
Der Code
Es folgt ein allgemeiner Code des Heap-Moduls. %DataType% steht dabei stellvertretend und muß durch den gewünschten Datentyp ersetzt werden.
Option Explicit Private Type MemSeg Flag As Byte Seg As %DataType% End Type '---------------------------------------------------- Public Heap() As MemSeg Private m_HeapSize As Long Private m_Dynamic As Boolean Private m_Steps As Long '---------------------------------------------------- '---------------------------------------------------- ' SetHeapSize ' definiert die logische Größe des Heaps ' - [IN] Size As Long: Anzahl der Segmente des Heaps '---------------------------------------------------- Public Sub SetHeapSize(Size As Long) m_HeapSize = Size ReDim Heap(1 To Size) End Sub '---------------------------------------------------- ' ReSize ' Ändert die Größe des Heaps -- dabei wird der ' aktuelle Inhalt beibehalten ' - [IN] Size As Long: Neue Anzahl von Segmenten '---------------------------------------------------- Public Sub ReSize(Size As Long) m_HeapSize = Size ReDim Preserve Heap(1 To Size) End Sub '---------------------------------------------------- ' SetDynamic ' legt fest, ob der Heap dynamisch (skalierbar) ist ' - [IN] Enabled As Boolean: Dynamik an/aus ' - [IN] Steps As Long: Vergrößerungsschritt '---------------------------------------------------- Public Sub SetDynamic(Enabled As Boolean, Steps As Long) m_Dynamic = Enabled m_Steps = Steps End Sub '---------------------------------------------------- ' Alloc As Long ' Reserviert Speicher auf dem Heap ' - gibt einen Zeiger auf das reservierte Segment zurück '---------------------------------------------------- Public Function Alloc() As Long For Alloc = 1 To m_HeapSize If Heap(Alloc).Flag = 0 Then Heap(Alloc).Flag = 1 Exit Function End If Next Alloc If m_Dynamic Then Call ReSize(m_HeapSize + 10) Heap(Alloc).Flag = 1 Else Call Err.Raise(vbObjectError, "GC::Alloc", "Heap overflow") End If End Function '---------------------------------------------------- ' Free ' gibt ein Heap-Segment frei ' - [IN] Segment As Long: Adresse des Segments '---------------------------------------------------- Public Sub Free(Segment As Long) Heap(Segment).Flag = 0 End Sub
Etwas fällt ins Auge: die Kürze des Codes. Es wird ja auch nicht viel gemacht: die Funktion Alloc() sucht lediglich nach dem ersten freien Speichersegment im Heap-Feld. Dieses erkennt er dadurch, dass die Eigenschaft Flag null ist. Alloc() setzt sie nun auf eins und gibt den Feldindex zurück: unser "Zeiger". Free() macht noch viel weniger: es setzt lediglich den Flag des angegebenen Feldeintrags auf null.
Dieser Code gibt uns Werkzeuge in die Hand, um einen Heap zu erzeugen (SetHeapSize()), Speicher zu reservieren (Alloc()) und diesen auch wieder zu befreien (Free()).
Einfaches Beispiel
Man kann nun einen C++-Code, der Zeiger verwendet, 1:1 nach VB übersetzen:
int main(void) { int* Int1; int* Int2; Int1 = new int(); Int2 = Int1; *Int1 = 5; cout << "Int1: " << *Int1 << endl; cout << "Int2: " << *Int2 << endl; delete Int1; return 0; }
Dieser Code ergibt in VB (Voraussetzung: wir haben einen Long-Heap erstellt):
Sub Main() Call SetHeapSize(10) Dim Lng1 As Long Dim Lng2 As Long Lng1 = Alloc() Lng2 = Lng1 Heap(Lng1).Seg = 5 Debug.Print "Lng1: "; Heap(Lng1).Seg Debug.Print "Lng2: "; Heap(Lng2).Seg Call Free(Lng1) End Sub
Einen grundlegenden Unterschied gibt es zwischen den C++-Zeigern und unseren VB-"Zeigern": Zeiger in C++ sind absolut. Die absolute Adresse einer Variable kann man in VB ebenfalls über einen Umweg ermitteln, die VBA-Bibliothek sieht dafür die undokumentierten Funktionen VarPtr(), StrPtr() und ObjPtr() vor. Allerdings kann VB mit diesen Zeigern nicht umgehen, man braucht APIs, um mit ihnen arbeiten zu können. Wir arbeiten mit Feldindizes. Ein Feldindex ist in Wahrheit nichts anderes als eine relative Speicheradresse, relativ nämlich zum ersten Element des Feldes. Diese Adressen sind selbstverständlich unbenutzbar in APIs, aber für unsere Zwecke reichen sie vollauf, da sie die selben Eigenschaften wie absolute Zeiger besitzen.
Achtung : Wie auch in C++ wird hier per Alloc() zwar Speicherplatz reserviert, nicht aber geleert! Das heißt, dass der reservierte Speicherplatz durchaus bereits einen Wert enthalten kann.
An dem Code fällt auf, dass der Derefenzierungsoperator aus C++, das Asterisk (*) durch Heap(...).Seg ersetzt wird. Das sieht umständlich aus (ist es auch), lässt sich aber leider nicht verhindern. Für Codes wie den obigen wäre die Benutzung des Heaps also recht umständlich. In einigen Gebieten jedoch wird Programmierung durch Benutzung des Heaps vereinfacht.
Oft wird man im Internet zum Beispiel Algorithmen in C++ (oder C) finden, die bestimmte Programmiertechniken umsetzen. Viele dieser Algorithmen verwenden Zeiger, was die Übersetzung der Algorithmen nach VB erschwert. Mithilfe des Heaps ist das hingegen kein Problem.
Ein Beispiel für einen solchen Algorithmus ist eine spezielle Listenklasse, eine so genannte Warteschlange. Hierbei handelt es sich um einen Datenbehälter ("Container"), in den man Daten hineingeben und und aus dem man Daten herausnehmen kann. Das besondere an der Warteschlange ist, dass man grundsätzlich nur hinten Daten anreihen kann und nur vorne Daten wegnehmen: In der selben Reihenfolge, in der man die Daten einfügt, kommen sie auch wieder heraus.
Die Implementierung einer solchen Struktur ist in VB ohne API oder Klassen nicht möglich, da Zeiger benötigt werden. Wenn man Klassen für die einzelnen Elemente der Warteschlange verwendet, hat man jedoch einen ungeheuren Geschwindigkeitsnachteil. Hier schafft unser Heap Abhilfe.
Archivierte Nutzerkommentare
Klicken Sie diesen Text an, wenn Sie die 4 archivierten Kommentare ansehen möchten.
Diese stammen noch von der Zeit, als es noch keine direkte Forenunterstützung für Fragen und Kommentare zu einzelnen Artikeln gab.
Aus Gründen der Vollständigkeit können Sie sich die ausgeblendeten Kommentare zu diesem Artikel aber gerne weiterhin ansehen.
Kommentar von Cyron am 27.10.2008 um 16:10
Die folgende Zeile ist fehlerhaft:
Call ReSize(m_HeapSize + 10)
sie sollte wohl eher
Call ReSize(m_HeapSize + m_Steps)
lauten.
LG Cyron
Kommentar von Christian am 05.10.2005 um 09:19
Das Tutorial ist wirklich gut, wenn man Zeiger unter VB emulieren will.
Ich frage mich aber, warum Microsoft die Zeiger nicht in VB integriert hat. Das finde ich bei der Programmierung (und gerade bei der dynamischen Speicherverwaltung) das Beste, was es diesbezüglich gibt.
Da sage ich nur:
"Ein Hoch auf C/C++ und die Win32-API!" :-)
Kommentar von Konrad L. M. Rudolph am 17.02.2005 um 16:40
Peter,
auf diese Funktionen weise ich aber auch explizit in dem Tutorial hin!
Kommentar von Peter Hoyer am 23.10.2004 um 14:27
Ganz verzweifelt über ein Pointerproblem, dass ich nicht über die Heap-Lösung behandeln konnte, weil es sich um Adreesen von zwei externen Objekten handelte, die ich vergleichen wollte, habe ich den folgenden interssanten Artikel über undokumentierte VB Funktionen gefunden:
http://www.vb-magazin.de/new/contentid-122.html