Pointer und manuelle Speicherverwaltung

Übersicht BlitzMax, BlitzMax NG FAQs und Tutorials

Neue Antwort erstellen

Jolinah

Betreff: Pointer und manuelle Speicherverwaltung

BeitragDo, Jul 21, 2005 8:41
Antworten mit Zitat
Benutzer-Profile anzeigen
Ich weiss dass es hier schon ein Pointer Tutorial gibt, aber ich hatte letztens auch sowas geschrieben und dachte ich poste es mal:

Das ganze erklärt Pointer nicht von Grund auf, also es wird erwartet dass man weiss wie man Pointer deklariert und für was sie ungefähr sind Wink

Code: [AUSKLAPPEN]
Strict


Local A:Int = 10
Local ptA:Int Ptr = Varptr(A)

'Direkt der Integer ausgeben
Print "Integer: " + A

'Den Pointer dereferenzieren (Den Wert auslesen von der Adresse wo der Pointer hinzeigt)
Print "Wert der Stelle an die der Pointer zeigt: " + Var(ptA)

'Das selbe wie Var
Print "Wert der Stelle an die der Pointer zeigt (2): " + ptA[0]


'Oben sieht man dass ein Zeiger eigentlich identisch wie ein Array gehandhabt werden kann.
'Dazu mehr unter "Vergleich mit einem Array"
'Folglich würde das auch funktionieren:
Print "Ein Wert aus einem unbekannten Speicherbereich:" + ptA[7]
      'ABER das sollte man nicht weil da ja kein Speicher reserviert wurde.
      'Hier befinden wir uns in einem unbekannten Speicherbereich, auslesen mag ja noch
      'gehen, verändern sollte man aber nichts. Könnte einen Absturz oder schlimmeres zur Folge
      'haben (unter Umständen) ;) Daher ist es immer wichtig dass ein Pointer wirklich
      'an eine Stelle zeigt die vorher für das Programm reserviert wurde (mit Local A:Int etc.)
      'Ein Pointer selbst benötigt zwar auch Speicher aber übernimmt nicht die Aufgabe
      'da wo er hinzeigt Speicher zu reservieren.
               


'Gibt die Speicheradresse wo der Pointer hinzeigt an (nicht den Wert der da gespeichert ist)
Print "Speicheradresse wo der Pointer hinzeigt:" + Int(ptA)




'-------- Vergleich mit einem Array ---------
Print "------- Vergleich mit Array --------"

'Ein Array mit 10 Ints (Index 0 - 9) erstellen
Local Array:Int[] = New Int[10]

'In wirklichkeit ist ein Array auch nur ein Pointer der dann dereferenziert wird.
'Ein Array ist aber trotzdem ein eigener Typ der dafür sorgt dass man nicht auf
'Speicher ausserhalb zugreifen kann. Also konvertieren wir das ganze zuerst in einen Int Ptr
'Wenn wir dann den Int Pointer haben können wir von diesem die Adresse auslesen
'indem wir den Int Ptr noch in einen Int casten (wandeln).
Print "Speicheradresse wo das Array hinzeigt: " + Int(Int Ptr(Array))


'Jetzt machen wir nochmal einen Int Ptr zum vergleichen.
'Und da ja ein Array nichts anderes als ein Pointer ist können wir selbstverständlich
'dem Pointer auch das Array zuordnen. Mit anderen Worten lassen wir den Pointer
'dann auf die selbe Stelle wie das Array zeigen.
Local ptArr:Int Ptr = Array

'Zum vergleich geben wir die Adresse nochmal aus (müsste identisch mit dem Array sein)
Print "Speicheradresse wo der Pointer hinzeigt: " + Int(ptArr)


'Jetzt schreiben wir wie üblich ein paar Werte in das Array
Array[0] = 20
Array[1] = 30
Array[2] = 40

'Und nun lesen wir diese wieder aus, aber nicht über das Array sondern über den Pointer :)
'Wie man sieht ist es vollkommen Identisch zu handhaben. Was man beachten muss ist aber
'dass bei einem Pointer auch über die Array-Grenzen hinaus Werte gesetzt und gelesen werden
'können. Es wird kein Fehler von BMax ausgelöst, sondern es wird auf Speicher zugegriffen
'der nicht zu diesem Programm gehört. Das sollte man verhindern in dem man in einer Variable
'zwischenspeichert wie gross das Array ist. Oder am besten man benutzt dafür auch Arrays
'statt Pointer, denn dafür sind sie ja da.
Print "Wert von Array Index 0: " + ptArr[0]
Print "Wert von Array Index 1: " + ptArr[1]
Print "Wert von Array Index 2: " + ptArr[2]



'--------- Zeiger-Arythmetik (oder so ähnlich ^^) ---------
Print "------ Zeiger-Arythmetik -------"

'Mit Zeigern lässt sich auch rechnen, genauer gesagt mit der Speicheradresse.
'Jedoch muss man auch hier wieder aufpassen dass sich die Speicheradresse nicht so verändert dass
'man am Ende auf Speicher zugreift der nicht zum Programm gehört.

'Ok, was genau bewirkt es wenn ich zu einem Pointer 1 hinzu addiere.
'Wie man meinen könnte wird nicht zur Speicheradresse 1 hinzugezählt,
'sondern 1 * Grösse des Typs auf den der Pointer zeigt. Das heisst wenn
'es ein Int Ptr ist wird 4 dazu gezählt, weil ein Int ja 4 Byte gross ist.
'Wäre es ein Byte Ptr würde nur 1 dazu gerechnet werden.
'Das gleiche gilt natürlich auch bei Subtraktion.
Print "Wert von Pointer original (Index 0): " + ptArr[0]
ptArr = ptArr + 1
Print "Wert nach Pointer + 1: " + ptArr[0]
ptArr = ptArr + 1
Print "Wert nach Pointer + 2 (Total): " + ptArr[0]

'So haben wir den Pointer an eine andere Stelle zeigen lassen
'Er zeigt jetzt auf das 3. Element des Arrays, weshalb man mit
'Index 0 nicht mehr den Wert von Array[0] sondern den Wert
'von Array[2] zurück bekommt. Um die Werte vor dem 3. Element
'wieder auslesen zu können muss man den Pointer wieder minus 2
'rechnen oder beim Index -2 benutzen.
'Folgendes gibt den Wert von Array[0] zurück:
Print "Wert von Pointer - 2 (relativ): " + ptArr[-2]

'Das ganze funktioniert auch mit Multiplikation und jedem anderen
'Rechenoperator, jedoch muss man immer daran denken dass 1 nicht gleich 1 ist,
'sondern 1 * Grösse des Typs vom Pointer



'------ Speicher selbst verwalten -------
Print "------ Speicher selbst verwalten -----"

'In bestimmten Fällen könnte es nötig sein selber Speicher zu reservieren.
'Ganz wichtig hier ist: Wenn man den Speicher selber reserviert muss man
'auch selber wieder dafür sorgen dass er wieder freigegeben wird.
'BlitzMax und sein GarbageCollector haben auf diesen Speicher keinen einfluss.
'Also wird BMax auch nicht für uns den Speicher freigeben und somit haben
'wir ein Memory Leak produziert.

'Wenn wir Speicher anfordern bekommen wir einen Byte Ptr auf diesen Speicher
'Byte Ptr aus dem Grund das der PC ja nicht wissen kann was wir da jetzt reinspeichern
'Und Byte ist nunmal die kleinste Speichereinheit auf dem PC also können wir damit
'alles anstellen was wir brauchen.
'Wir müssen dann natürlich sagen wieviel Bytes wir denn wollen.
'Reservieren wir mal 8 Bytes (2 Integer)
Local ptMem:Byte Ptr = MemAlloc(8)


'Jetzt machen wir einen Int Ptr zur besseren Handhabung und konvertieren den Byte Ptr
'in einen Int Ptr
Local ptInt:Int Ptr = Int Ptr(ptMem)

'Wir setzen für die beiden Ints einen Wert:
ptInt[0] = 10
ptInt[1] = 20

'Jetzt geben wir mit dem Byte Ptr alle Bytes (0-7) des reservierten Speichers aus.
Print "Alle Bytes des reservierten Speichers"
For Local i = 0 To 7
   Print ptMem[i]
Next

'Man sieht das im ersten Byte des jeweiligen Integers die Werte gespeichert sind.
'Da ein Byte Werte bis 255 annehmen kann hat es also im ersten Byte platz und
'die anderen 3 bleiben 0.


'Wie erwähnt muss der Speicher selbst wieder freigegeben werden.
'Hier empfehle ich keine Zeiger-Arythmetik vorzunehmen oder die Adresse
'des Byte Pointers in irgend einer anderen Weise zu ändern. Denn der Ptr wird zum
'Freigeben wieder benötigt und er sollte nicht auf eine falsche Stelle zeigen.
'Natürlich muss man auch wieder die Grösse mit angeben damit der PC weiss
'wieviel Bytes er von der Stelle an wo der Pointer hinzeigt wieder freigeben soll.
'Will man trotzdem Zeiger-Arythmetik etc. benutzen sollte man sich einen zweiten
'identischen Zeiger erstellen oder sicher sein das der Zeiger am Ende wieder auf
'die Stelle zeigt wo er ursprünglich hinzeigte.
MemFree(ptMem, 8)


'----- Zu guter Letzt.. ------

'Wenn man einen Zeiger benutzen will sollte man auch sicher sein dass er
'überhaupt auf etwas Zeigt. Hat ein Zeiger den Wert Null oder 0 so zeigt
'er auf nichts. Man sollte dann auch nicht versuchen den Zeiger zu
'dereferenzieren (Zeiger[Index] oder Var(Zeiger))

'Zudem sollte man einen Zeiger auch gleich wieder auf Null setzen
'sobald er nicht mehr benötigt wird.

'Es ist nützlich wenn man sich mit Zeigern auskennt, trotzdem sollte man sie
'in einer Sprache wie BlitzMax nicht allzuviel benutzen. Weil es einfach nicht
'nötig ist, der Grund:
'BlitzMax verwaltet seinen Speicher eigentlich schon selber und ist TypeSafe.
'Das heisst man kann damit keine allzugrossen Speicherfehler machen.
'Und es gibt zu allem was man mit Pointern machen kann eine sehr gute Alternative,
'nämlich TBank und Referenzen. Im Grunde ist TBank sowas ähnliches wie wenn man manuell
'Speicher reserviert und dann mit einem Byte Pointer arbeitet.
'Referenzen sind Typesafe und werden automatisch verwaltet, man kann damit also keine Speicherfehler produzieren.
'Sie sind in der Regel den Pointern vorzuziehen.
'Da dies aber ein Pointer Tutorial ist gehe ich nicht näher darauf ein.

'------- Nachtrag: Speicher aufräumen :-) -------

'Das hat jetzt nicht mehr unbedingt etwas mit Pointern zu tun aber ist dennoch
'ziemlich wichtig. Speicher den man nicht mehr braucht sollte freigegeben werden.
'In BMax wird der Speicher sofern man ihn nicht wie oben beschrieben selber
'reserviert hat automatisch verwaltet.

'Um den Speicher eines Objektes wie String, TImage etc. freizugeben muss man
'die Variable einfach auf Null setzen. Zeigen sonst keine Variablen mehr auf dieses Objekt
'so wird beim nächsten Flushmem der Speicher dieses Objekts automatisch freigegeben:

'Als kleines Beispiel hätten wir hier noch unser Int Array welches immer noch im Speicher rumlungert ;)
'Setzen wir es einfach auf Null.
Array = Null

'Für alle Fälle setzen wir auch den Zeiger auf das Array auf Null, damit das Objekt
'auch wirklich entfernt wird, man weiss ja nie ^^
ptArr = Null

'Das Array ist immer noch im Speicher.
'Mit Flushmem wird es nun Freigegeben:
FlushMem

'Normale Datentypen (keine Objekte) wie Int, Float, Short etc. werden automatisch wieder freigegeben.
'Ein Int muss also nicht auf Null gesetzt werden.


'Hier macht es jetzt aber nicht soviel Sinn da beim Programmende sowieso
'der ganze Speicher der gebraucht wurde wieder freigegeben wird, mit
'Ausnahme vom selber reservierten ;)

'In Spielen genügt zudem 1 Flushmem am Ende der Hauptschleife.
'Benutzt man es in jeder Funktion etc. könnte das evtl. sogar ein
'Performanceverlust sein.

'Ende :-)
End

Neue Antwort erstellen


Übersicht BlitzMax, BlitzMax NG FAQs und Tutorials

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group