Zeiger und Speicherzugriff

Pointer (Zeiger)

Beispiel

  *MyScreen.Screen = OpenScreen(0, 320, 200, 8, 0)
  mouseX = *MyScreen\MouseX  ; setzt voraus, dass die Screen Struktur ein MouseX Feld beinhaltet
Es gibt nur drei gültige Methoden, den Wert eines Zeigers zu setzen:
- als Ergebnis einer Funktion erhalten (wie im obigen Beispiel gezeigt)
- den Wert von einem anderen Zeiger kopieren
- Adresse einer Variable, einer Prozedur oder einer Sprungmarke (siehe weiter unten) ermitteln

Hinweis: Anders als in C/C++ ist in PureBasic der * immer Teil des Variablennamens. Damit sind '*ptr' und 'ptr' zwei verschiedene Variablen. 'ptr' ist eine (reguläre) Variable zum Speichern eines Werts, '*ptr' ist eine andere Variable vom Typ Zeiger zum Speichern einer Adresse.

Zeiger und Speichergröße

Da Zeiger als Werte nur Adressen empfangen, ist die Speichergröße eines Zeigers der Platz, der das Speichern einer absoluten Adresse des Prozessors ermöglicht:
- Auf 32 Bit Prozessoren ist der Adressraum auf 32 Bit begrenzt, weshalb ein Zeiger 32 Bit (4 Bytes, wie ein 'Long') im Speicher verbraucht
- Auf 64 Bit Prozessoren benötigt er 64 Bit (8 Bytes, wie ein 'Quad') im Speicher, weil die absolute Adresse in einem 64 Bit Bereich liegt.

Als eine Konsequenz daraus hängt der Typ des Zeigers vom CPU-Adressmodus ab ('Long' auf 32 Bit CPU und 'Quad' auf 64 Bit zum Beispiel), so ist ein Zeiger eine Variable vom Typ Zeiger.
Daraus resultiert, dass das Zuweisen eines nativen Typs zu einem Zeiger (*Pointer.l , *Pointer.b ...) nicht erlaubt ist.

Hinweis:
- Jedes Mal, wenn eine Speicheradresse in einer Variable gespeichert werden muss, sollte dies mit einem Zeiger erfolgen. Dies garantiert Adressintegrität zur Kompilationszeit, unabhängig davon wie gerade der CPU-Adressmodus ist.
- PureBasic x86 erstellt keine 64 Bit Executables. Für damit kompilierte Programme gewährt das Betriebssystem nur eine Adressierung mit 32 Bit Pointern.

Zeiger und Strukturen

Durch das Zuweisen einer Struktur zu einem Zeiger (zum Beispiel *MyPointer.Point) wird der Zugriff auf jede Speicheradresse auf strukturiertem Weg (mit dem Operator '\') ermöglicht.

Beispiel

  Define Point1.Point, Point2. Point
  *CurrentPoint.Point = @Point1  ; Zeiger-Deklaration, verknüpft mit einer Struktur und initialisiert mit der Adresse von Point1
  *CurrentPoint \x = 10          ; Point1\x erhält den Wert 10 zugewiesen
  *CurrentPoint.Point = @Point2  ; Springen zur Adresse von Point2
  *CurrentPoint \x = 20          ; Point2\x erhält den Wert 20 zugewiesen
  Debug Point1\x
  Debug Point2\x
Zeiger erlauben das einfache Bewegen, Lesen und Schreiben im Speicher. Weiterhin ermöglichen sie dem Programmierer den Zugriff auf große Datenmengen ohne den zusätzlichen Aufwand der Datenvervielfältigung. Das Kopieren eines Zeigers ist viel schneller.

Zeiger sind auch in Strukturen verfügbar, für weitere Informationen siehe das Kapitel Strukturen.

Zeiger und Zeichenketten (Strings)

Alle Variablen haben eine permanente Größe im Speicher (2 Byte für Word, 4 Bytes für Long, etc.) - außer für Strings, da sich deren Länge verändern kann. Aus diesem Grund werden String-Variablen auch auf eine andere Weise gehandhabt wie sonstige Variablen.
Daher speichert ein Struktur-Feld, welches auf einen String verweist, nur die Speicheradresse des Strings anstelle des Strings selbst: ein solches Struktur-Feld ist ein Zeiger auf einen String.

Beispiel

  Text$ = "Hello"
  *Text = @Text$            ; *Text speichert die Adresse des Strings im Speicher
  *Pointer.String = @*Text  ; *Pointer verweist auf *Text
  Debug *Pointer\s          ; Darstellen des Strings, welcher sich an der in *Pointer gespeicherten Adresse befindet (z.B. @Text$)
Zeiger Arithmetik

Arithmetische Operationen mit Zeigern sind möglich und praktisch durch Verwendung von SizeOf().

Beispiel

  Dim Array.Point(1)         ; Array von Punkten
 
  *Pointer.Point = @Array()  ; Speichern der Array-Adresse
  *Pointer\x = 10            ; Ändern des Werts vom ersten Array-Element
  *Pointer\y = 15   

  *Pointer + SizeOf(Point)   ; Springen zum nächsten Array-Element

  *Pointer\x = 7             ; Ändern des Werts vom zweiten Array-Element
  *Pointer\y = 9
 
  ; Anzeigen der Ergebnisse
  For i = 0 To 1
    Debug Array(i)\x
    Debug Array(i)\y
  Next i

Adressen von Variablen

Um die Adresse einer Variable in Ihrem Programmcode zu ermitteln, benutzen Sie das 'at' Symbol (@). Ein üblicher Grund für die Benutzung dieser Methode ist, wenn Sie die Variable eines Struktur-Typs an eine Prozedur übergeben wollen. Sie müssen einen Zeiger auf diese Variable übergeben, da Sie strukturierte Variablen nicht direkt übergeben können.

Beispiel

  Structure astruct
    a.w
    b.l
    c.w
  EndStructure
  
  Procedure SetB(*myptr.astruct)
    *myptr\b = 69
  EndProcedure
  
  Define.astruct myvar
  
  SetB( @myvar )
  Debug myvar\b

Adressen von literalen Strings (Buchstabenketten)

Um die Adresse eines literalen Strings zu ermitteln, können Sie das 'at' symbol (@) davor verwenden. String-Konstanten werden ebenfalls unterstützt.

Beispiel

  *String = @"Test"
  Debug PeekC(*String) ; Wird 84 ausgeben, was der Wert von 'T' ist

Adressen von Prozeduren

Für fortgeschrittene Programmierer. Die häufigste Ursache für die Notwendigkeit zum Ermitteln einer Adresse besteht darin, wenn auf "low-level" Ebene mit dem OS gearbeitet werden soll. Einige OS erlauben "Callback"- oder "Hook"-Funktionen (für einige Operationen) zu definieren, welche durch das OS aufgerufen werden und dem Programmierer den Ausbau der Fähigkeiten der betreffenden OS-Routine ermöglichen. Die Adresse einer Prozedur wird auf ähnliche Art und Weise wie bei Variablen ermittelt.

Beispiel

  Procedure WindowCB(WindowID.i, Message.i, wParam.i, lParam.i)
    ; Hier wird Ihre Callback Prozedur abgearbeitet.
  EndProcedure
  
  ; Ein spezielles Callback für Windows OS ermöglicht die Verarbeitung von Window-Ereignissen.
  SetWindowCallback( @WindowCB() )

Adressen von Sprungmarken (Labels)

Es kann auch nützlich sein, die Adresse einer Sprungmarke innerhalb Ihres Programmcodes zu ermitteln. Dies ist möglich, wenn Sie Zugriff auf an dieser Sprungmarke gespeicherten Code oder Daten erhalten möchten. Oder jede andere gute Möglichkeit, die Sie sich vorstellen können. Um die Adresse einer Sprungmarke zu ermitteln, schreiben Sie ein Fragezeichen (?) vor den Namen der Sprungmarke.

Beispiel

  Debug "Größe der Daten = " + Str(?EndOfMyData - ?MyData)
  End
  
  DataSection
    MyData:
      IncludeBinary "somefile.bin"
    EndOfMyData:
  EndDataSection