FreeBasic: Pointer
Aus Wikibooks
Inhaltsverzeichnis |
[Bearbeiten] Einleitung
[Bearbeiten] Adresse
Gleich zu Beginn ein kurzes Beispielprogramm:
Dim a as byte Dim b as byte a=1 b=a+5 ? "Die Variable a hat den Wert ";a;" und hat die Adresse "; @a ? "Die Variable b hat den Wert ";b;" und hat die Adresse "; @b sleep
Man kann sich den Speicher etwa so vorstellen:
Jedes Byte hat eine "Nummer" und diese "Nummer" kann man mit @ abfragen.
Der Begriff "Nummer" steht in Anführungszeichen, weil die Fachbegriff dazu "Adresse" lautet.
[Bearbeiten] Pointer
Später wird es nützlich, wenn man diese Adresse in einer Variable speichern kann:
Dim a as byte Dim b as Byte PTR a=1 b=@a ? "Die Variable a hat den Wert ";a;" und hat die Adresse "; b sleep
Die Variable, die die Adresse speichert, hat wieder einen speziellen Namen:
Pointer oder auf Deutsch Zeiger.
Da ein Pointer eine spezielle Variable ist, muss sie auch speziell deklariert werden.
Je nachdem, auf was der Pointer zeigt, lautet die Deklaration zum Beispiel:
Dim b as Byte PTR Dim b as Integer PTR
Wenn man den Pointer für verschiedene Variablentypen verwendet, kann man das so lösen:
Dim b as any ptr
Jedoch ist das nicht unbedingt das Wahre, weil man dann die Kontrolle durch den Compiler umgeht.
Bis jetzt sind Pointer nutzlos, denn man kommt ja nicht an den Wert heran auf den der Pointer zeigt; aber das ändert sich nun:
Dim a as single Dim b as single ptr Dim c as single b=@a a=12.256 c=*b ? c sleep
Mit * kann man also den Wert, auf den der Pointer zeigt, holen.
[Bearbeiten] Wiederholungsfragen
- Was ist eine Adresse?
- Was ist ein Pointer?
[Bearbeiten] Alternative Befehle
Wie so oft, gibt es auch bei Pointern verschiedene Lösungen für das gleiche Problem.
[Bearbeiten] Varptr()
Anstelle von @ kann man auch die Funktionen VARPTR() anwenden.
Beispiel:
Dim a as byte Dim b as Byte PTR a=1 b=varptr(a) ? "Die Variable a hat den Wert ";a;" und hat die Adresse "; b sleep
[Bearbeiten] Peek und Poke
Als Alternative zu * gibt es die beiden Befehle PEEK und POKE.
Beispiel:
Dim a as byte Dim b as Byte PTR a=1 b=@a ? peek(b) poke b, 49 ? peek(b) sleep
Poke schreibt auf das RAM und Peek holt vom RAM.
Peek und Poke haben ein weiteres Parameter, das ebenfalls vorgestellt werden soll.
Dieser Parameter ist vorallem im Zusammenhang mit ANY PTR interessant.
Dieses Beispiel verwendet einen ANY PTR um an die einzelnen Bytes eine Integer Variable zu kommen:
Dim a as integer Dim b as Any PTR a=1023 b=@a ? peek(ubyte,b) ? peek(ubyte,b+1) ? peek(ubyte,b+2) ? peek(ubyte,b+3) sleep
Betrachtet die Ausgabe:
255 3 0 0
Zuerst wird also das niederwertigste und als letztes wird das hochwertigste Byte ausgegeben.
[Bearbeiten] Vorteile von Peek und Poke
- Typ bestimmbar
[Bearbeiten] Nachteile von Peek und Poke
- Mehr zu tippen
- Gilt als zum Teil als "Überbleibsel" von QBasic
[Bearbeiten] Direkter Vergleich beider Methoden
Sehen wir uns das an Hand eines Beispieles an:
Dim a as integer Dim b as integer ptr Dim c as integer Dim d as byte Dim e as byte ptr Dim f as byte a=0 d=0 ? @a ? @d b=@a e=@d *b=1200 *e=-26 ? a ? d c=*b f=*e ? c ? f sleep |
Dim a as integer Dim b as integer ptr Dim c as integer Dim d as byte Dim e as byte ptr Dim f as byte a=0 d=0 ? varptr(a) ? varptr(d) b=varptr(a) e=varptr(d) Poke Integer,b,1200 Poke Byte,e, -26 ? a ? d c=Peek(Integer,b) f=Peek(Byte,e) ? c ? f sleep |
[Bearbeiten] Arrays
[Bearbeiten] Byte Array
Dim a(10) as byte Dim b as byte ptr a(0)=12 a(1)=2 a(2)=52 a(3)=98 a(4)=21 a(5)=3 a(6)=9 a(7)=63 a(8)=-23 a(9)=-3 a(10)=-1 b=@a(0) for i=0 to 10 ? a(i), *(b+i), b[i] Next i sleep
Hier sollte nun grosser Erklärungsbedarf herrschen, wobei wir eigentlich unser Augenmerk nur auf diese Zeilen haben müssen:
b=@a(0) for i=0 to 10 ? a(i), *(b+i), b[i] Next i
Zuerst wird also der Pointer auf den Anfang des Byte-Arrays gesetzt; soweit so gut.
Auch das a(i) ist logisch.
Was passiert aber hier? *(b+i)
Bei einem Byte-Array liegen alle Bytes sauber sortiert direkt nebeneinander im Speicher.
Ein Byte neben a(0) liegt somit a(1); mit anderen Worten:
Die Adresse von a(0) plus 1 ist die Adresse von a(1), folglich ist die Adresse a(0)+i gleich der Adresse von a(i).
Nun zum b[i]:
Verwechselt das bloss nicht mit einem Array, denn b[i] ist nur die Kurzform von *(b+i).
[Bearbeiten] Integer-Array
Das Prinzip ist genau gleich wie beim Byte Array:
Dim a(10) as integer Dim b as integer ptr a(0)=12458 a(1)=-12331 a(2)=132014 a(3)=-98631 a(4)=546748 a(5)=468484 a(6)=-458484 a(7)=-468646 a(8)=-765432 a(9)=-678901 a(10)=463464 b=@a(0) for i=0 to 10 ? a(i), *(b+i), b[i] Next i sleep
Genau gleich also wie bei einem Byte Array und das obwohl eine Integer Variable 4 Byte gross ist. (Mehr dazu im Kapitel Pointer Arithmetik)
[Bearbeiten] Pointer Arithmetik
Oder auf Deutsch: Mit Zeigern rechnen.
Dim a as integer Dim b as integer ptr a=1023 b=@a ? b b=b+1 ? b sleep
[Bearbeiten] Pointer auf Strings
[Bearbeiten] Einleitung
Bisher haben wir Strings immer als eigenständigen Variablentyp betrachtet.
Aber ist dem so?
Nein!
Aber was ist dann ein String?
[Bearbeiten] ZString
Erstmal erklärt an der einfachsten String Variante dem ZString:
Nehmen wir das als Beispiel:
Dim a as zstring*10
entspricht in etwa
Dim a(0 to 9) as ubyte
Wobei a(9) immer 0 ist, weil ja ein ZString immer mit einem Chr(0) endet.
Die Variable a kann man aber nicht als ZString verwenden, weil Freebasic das bei den Typenprüfung als Fehler meldet.
Also umgehen wir mal die Typenprüfung:
Dim a(0 to 20) as ubyte Dim b as any ptr dim c as zstring ptr b=@a(0) c=b Input *c ? *c sleep
und schon wirkt das UByte-Array wie ein ZString.
Ein ZString ist also mehr oder weniger ein Pointer auf ein UByte-Array.
[Bearbeiten] Nutzen in der Praxis
Wie wir letztes Mal festgestellt haben, ist ein String ein Pointer auf ein UByte-Array.
Je nach Anwendung kann es interessant sein, eben auf die einzelnen Bytes zuzugreifen.
Versuchen wir es doch einfach mal:
Dim a as string input a for i=0 to len(a)-1 ? a[i] next i sleep
Geht also!
Das len(a)-1 verhindert, dass der Pointer abhaut, also weiter liest, obwohl der String zu Ende ist.
Natürlich kann man mit dieser Methode den String nicht nur lesen, sondern auch bearbeiten.
[Bearbeiten] String allgemein
Die Besonderheit des typischen Freebasic-Strings ist die variable Länge.
Das heißt:
In Freebasic kann ein String bei Bedarf größer oder kleiner werden.
Normalerweise bemerkt man das nicht, aber beim Umgang mit Pointern muss man das wissen!
Wie ist das Problem also gelöst worden?
Jeder String hat einen sogenannten "Descriptor" oder zu Deutsch "Bezeichner". Dieser "Bezeichner" enthält den Pointer zum eigentlichen String und auch die momentane Länge des Strings.
Der eigentliche String endet mit Chr(0), womit er sich zumindest beim lesen, wie ein ZString verhält.
Der genaue Aufbau des Bezeichners:
- Bezeichner
- Pointer zum eigentlichen String
- Länge
Alle drei Werte sind vom Typ UInteger.
Wenn ihr den zweiten Wert habt, also Pointer zum eigentlichen String, könnt ihr den String ähnlich wie einen ZString verwenden.
(Freebasic markiert für genau diesen Zweck das Ende eines Strings mit Chr(0))
[Bearbeiten] Beispiel zu Bezeichner
Nicht für echte Programme!
Dim Vari as String dim anyPTR as any ptr Dim PTRaufStr as zstring ptr Dim laenge as uinteger dim Bezeichner as uinteger Vari ="Hallo Welt" '----------- 'Bezeichner anyptr=@Vari anyptr-=4 Bezeichner=Peek(UInteger,anyPTR) ? Bezeichner ? '----------- 'Pointer zum eigentlichen String anyPTR=@Vari PTRaufStr=Peek(UInteger,anyPTR) ? PTRaufStr ? strptr(Vari) ? '----------- 'Länge anyptr=@Vari anyptr+=4 laenge=Peek(UInteger,anyPTR) ? laenge ? len(Vari) sleep
[Bearbeiten] Gültigkeit eines Stringpointers
Aus Prinzip dürfen "normale" Strings mittels Pointer oder anderem Direktzugriff nur gelesen, nicht aber geschrieben werden.
Der Pointer auf den eigentlichen String gilt nur solange wie der String nicht bearbeitet wird. Um unnötige Probleme zu vermeiden, sollte man sogar definieren, dass man die Adresse erst unmittelbar vor der Verwendung abfragt und das die Adresse nachher nicht mehr gültig ist.
Des Weiteren ist es sinnvoll, während man in dynamisch verwaltete Speicherbereiche eingreift, alle anderen Tasks des Programmes zustoppen. Siehe dazu auch FreeBasic: Multithreading.
Hier nun ein Beispiel, wie sich die Adresse eines Strings ändert, wenn sich die Länge ändert:
#define NULL 0 dim tmpStr as string dim Test_1 as zstring ptr = NULL dim Test_2 as zstring ptr = NULL tmpStr = "Test" Test_1 = strptr(tmpStr) tmpStr = " Ich bin eine lange Leitung die viel zu lang ist" Test_2 = strptr(tmpStr) ? Test_1 ? Test_2 print *Test_1; *Test_2 sleep
[Bearbeiten] Pointer auf Stringliterale
Dieses Beispiel ist Primär der Vollständigkeit wegen, praktische Anwendungen gibt es nur sehr wenige:
dim a as zstring ptr a = @"Zeichenkette fester Laenge" print *a
[Bearbeiten] Pointer auf Funktionen/Subroutinen
Er kann sinnvoll sein, vorallem in der Gui-Entwicklung, den Zeiger einer Funktion zu kennen und zu speichern. Hier sieht man die Praxis:
Declare function test (q as string) as string 'Nur zu Anschauungszwecken Dim test10 as function (q as string) as string 'Trick Teil 1 Dim a as any ptr 'Nur zu Anschauungszwecken a=@test 'Nur zu Anschauungszwecken test10=a 'Trick Teil 2 ? test10("Das ist") 'Aufruf sleep function test (q as string) as string 'Nur zu Anschauungszwecken ? "Test" 'Nur zu Anschauungszwecken test=q + "Hallo Welt" 'Nur zu Anschauungszwecken end function 'Nur zu Anschauungszwecken
Dieser Quellcode wurde von MichaelFrey am 11.06.2006 mit der Freebasic Version 0.16 Beta getestet.
[Bearbeiten] Pointer auf Pointer
Für diesen speziall Fall gibt es einen eigenen Pointertyp:
PTR PTR
Erstellen wir mal einen:
dim a as integer ptr dim b as integer ptr ptr dim c as integer c=12 a=@c b=@a ? *a ? **b sleep
Wie kann man das nun antreffen?
- String Arrays
- Mehrdimensionale Arrays (jenach Programmierung geht es auch mit einem)
[Bearbeiten] Speziellere Beispiele
[Bearbeiten] Ein Integer Array Byte für Byte kopieren
Dim a(10) as integer Dim b as any ptr dim c as byte ptr dim d as byte ptr dim e as integer a(0)=12458 a(1)=-12331 a(2)=132014 a(3)=-98631 a(4)=546748 a(5)=468484 a(6)=-458484 a(7)=-468646 a(8)=-765432 a(9)=-678901 a(10)=463464 b=@a(0) c=b b=@e d=b for i=0 to 10 *(d+0)=*(c+i*4+0) *(d+1)=*(c+i*4+1) *(d+2)=*(c+i*4+2) *(d+3)=*(c+i*4+3) ? a(i), e Next i sleep '''Zu Erklärung:''' b=@a(0) 'b (Any Pointer) auf a (Integer-Array) c=b 'c (Byte-Pointer) = b (Any Pointer) ' -> Auf das Integer-Array a zeigt jetzt ein Byte-Pointer ' Wieso? Wir wollen an die einzelnen Bytes der Integer-Arrays. b=@e 'b (Any Pointer) auf e (Integer-Variable) c=b 'c (Integer-Pointer) = b (Any Pointer) ' -> Auf die Integer-Variabel e zeigt jetzt ein Byte-Pointer ' Wieso? Wir wollen an die einzelnen Bytes der Integer-Variablen. ;Die einzelnen Bytes der Integer-Variabeln kopieren *(d+0)=*(c+i*4+0) *(d+1)=*(c+i*4+1) *(d+2)=*(c+i*4+2) *(d+3)=*(c+i*4+3)
[Bearbeiten] Todo List
Pointer auf Pointer
Pointer auf Funktionen/Subroutinen
Risiken
STRPTR()
Kapitel "Strings" mit Doku auf www.freebasic.net vergleichen
...