FreeBasic: Pointer
Einleitung
BearbeitenAdresse
BearbeitenGleich 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.
Pointer
BearbeitenSpä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.
Wiederholungsfragen
Bearbeiten- Was ist eine Adresse?
- Was ist ein Pointer?
Alternative Befehle
BearbeitenWie so oft, gibt es auch bei Pointern verschiedene Lösungen für das gleiche Problem.
Varptr()
BearbeitenAnstelle 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
Peek und Poke
BearbeitenAls 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.
Vorteile von Peek und Poke
Bearbeiten- Typ bestimmbar
Nachteile von Peek und Poke
Bearbeiten- Mehr zu tippen
- Gilt als zum Teil als "Überbleibsel" von QBasic
Direkter Vergleich beider Methoden
BearbeitenSehen 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
|
Arrays
BearbeitenByte Array
Bearbeiten 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).
Integer-Array
BearbeitenDas 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)
Pointer Arithmetik
BearbeitenOder auf Deutsch: Mit Zeigern rechnen.
Dim a as integer
Dim b as integer ptr
a=1023
b=@a
? b
b=b+1
? b
sleep
Pointer auf Strings
BearbeitenEinleitung
BearbeitenBisher haben wir Strings immer als eigenständigen Variablentyp betrachtet.
Aber ist dem so?
Nein!
Aber was ist dann ein String?
ZString
BearbeitenErstmal 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.
Nutzen in der Praxis
BearbeitenWie 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.
String allgemein
BearbeitenDie 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))
Beispiel zu Bezeichner
BearbeitenNicht 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
Gültigkeit eines Stringpointers
BearbeitenAus 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
Pointer auf Stringliterale
BearbeitenDieses 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
Pointer auf Funktionen/Subroutinen
BearbeitenEr 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.
Pointer auf Pointer
BearbeitenFü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)
Speziellere Beispiele
BearbeitenEin Integer Array Byte für Byte kopieren
Bearbeiten 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)
Todo List
BearbeitenPointer auf Pointer
Pointer auf Funktionen/Subroutinen
Risiken
STRPTR()
Kapitel "Strings" mit Doku auf www.freebasic.net vergleichen
...