Zum Inhalt springen

FreeBasic: Pointer

Aus Wikibooks

Einleitung

[Bearbeiten]

Adresse

[Bearbeiten]

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.

Pointer

[Bearbeiten]

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.

Wiederholungsfragen

[Bearbeiten]
  • Was ist eine Adresse?
  • Was ist ein Pointer?

Alternative Befehle

[Bearbeiten]

Wie so oft, gibt es auch bei Pointern verschiedene Lösungen für das gleiche Problem.

Varptr()

[Bearbeiten]

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

Peek und Poke

[Bearbeiten]

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.

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

[Bearbeiten]

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

Arrays

[Bearbeiten]

Byte 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

[Bearbeiten]

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)


Pointer Arithmetik

[Bearbeiten]

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

Pointer auf Strings

[Bearbeiten]

Einleitung

[Bearbeiten]

Bisher haben wir Strings immer als eigenständigen Variablentyp betrachtet.

Aber ist dem so?

Nein!

Aber was ist dann ein String?

ZString

[Bearbeiten]

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.

Nutzen in der Praxis

[Bearbeiten]

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.

String allgemein

[Bearbeiten]

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:

  1. Bezeichner
  2. Pointer zum eigentlichen String
  3. 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

[Bearbeiten]

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

Gültigkeit eines Stringpointers

[Bearbeiten]

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

Pointer auf Stringliterale

[Bearbeiten]

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

Pointer auf Funktionen/Subroutinen

[Bearbeiten]

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.


Pointer auf Pointer

[Bearbeiten]

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)

Speziellere Beispiele

[Bearbeiten]

Ein 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

[Bearbeiten]

Pointer auf Pointer

Pointer auf Funktionen/Subroutinen

Risiken

STRPTR()

Kapitel "Strings" mit Doku auf www.freebasic.net vergleichen

...