Entwickler-Ecke
Delphi Language (Object-Pascal) / CLX - Pointer und Strings
knittel - Mo 15.07.13 01:59
Titel: Pointer und Strings
Hallo allerseits,
Ich schreibe mir eine Wrapper-Klasse um TFileStream (habe den topic trotzdem hier gepostet, weil das Problem etwas anderes betrifft), habe aber damit ein Problem. Ich habe mir eine Funktion zum Speichern geschrieben und eine zum Laden und die sieht wie folgt aus:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21:
| function TMeFile.LoadNextElement(p: pointer): Byte; var i, n: integer; str: string; begin FStream.Read(result, 1); case result of 0: begin FStream.Read(n, 4); FStream.Read(p^, n); end; 1: begin FStream.Read(n, 4); SetLength(str, n); if (n > 0) then FStream.Read(pointer(str)^, n); SetLength(string(p^), n); Move(str[1], p^, n); end; 2: FStream.Read(p^, 4); 3: FStream.Read(p^, 4); end; end; |
Ich möchte halt, dass ich einfach nur schön MyFile.LoadNextElement(@MyInteger), MyFile.LoadNextElement(@MyString) benutzen kann. Mit meinem Code funktioniert das leider nicht so ganz. Ich hab zwar vom Umgang mit Pointern ein bisschen Ahnung, aber nur solange es sich um Listen/etc. handelt, leider nicht bei Strings. Könnt ihr mir irgendwie helfen und schauen, was ich an meinem Code verändern muss?
Danke schonmal! :)
jaenicke - Mo 15.07.13 08:43
Das Problem ist die Referenzzählung. Ein String, den du innerhalb der Methode erstellst, wird an deren Ende freigegeben. Wenn du einen Pointer darauf zurücklieferst, ist der daher nach dem Ende der Methode ungültig. (Davon abgesehen gehst du davon aus, dass ein Zeichen immer ein Byte hat, das würde mit neueren Delphiversionen nicht mehr funktionieren.)
In Zeile 8 schreibst du einfach in den Pointer, obwohl du gar nicht weißt, ob dort genug Platz ist. Keine gute Idee...
Grundsätzlich sollte immer die aufrufende Methode den Speicher reservieren und deine Methode diesen nur füllen. In neueren Delphiversionen würden sich dafür Generics oder anonyme Methoden anbieten, aber beides hatte Delphi 7 (wie in deinem Profil) damals nicht.
Was du machen kannst: Genauso wie die ReadBuffer und WriteBuffer Methoden nicht nur den Pointer angeben, sondern auch wie viel Platz darin ist. Ist nicht genug Platz, setzt du die Streamposition auf den Wert vor dem Leseversuch zurück und lieferst die benötigte Puffergröße zurück. So funktioniert es auch in der Windows API. Sprich:
Anfrage mit nil und Puffergröße 0, Rückgabe von 10 als Größe in Byte, SetLength auf den String, erneuter Aufruf mit dem Pointer auf den String und Puffergröße 10.
Wirklich elegant ist eine solche Kapselung allerdings erst mit Generics...
IhopeonlyReader - Mo 15.07.13 15:31
Ich würde sagen, der Fehler beginnt schon beim Aufbau der Procedure, du rufst die procedure auf ohne zu wissen was du empfängst musst aber schon etwas übergeben (z.B. Integer-Pointer)
Deshalb würde ich dir empfehlen den Identifikationsbyte VOR aufruf der procedure auszulesen und danach entscheiden welche Variable übergeben werden muss, sonst wird's mit dem weiterarbeiten schwer...
die Case-Abfrage kannst du also eigentlich aus der Procedure rausziehen..
Dann z.B. bei einem unbekannten Format, das ganze "ignorieren", da du damit eh nicht weiterarbeiten wirst, da du ja nicht weißt was es ist..
bei einem String ruft du dann eine extra dafür geschriebene Procedure auf und übergibst als Parameter schon ein String
Ist es eine Integer-Variable rufst du eine LoadIntegerFromStream-Procedure auf (selbergeschriebene) und übergibst als Parameter eine Integer Variable..
Eine Umsetzung mit Pointerübergabe halte ich allgemein für ungut.. Tut mir leid, aber ich sehe da keinen Sinn drin, etwas in eine Gruppe einzuordnen, wenn man den Typ eh wieder "verschinden lässt" über den Pointer...
Da könntest du auch
Delphi-Quelltext
1: 2: 3:
| FStream.Read(n, 4); if n<=SizeOf(p^) then FStream.Read(p^, n); |
wäre so wie ich das sehe das gleiche, da du den Typ außerhalb deine Procedure scheinbar nicht beachtest
knittel - Mo 15.07.13 16:44
Danke! Ich hab das jetzt genausogemacht wie du vorgeschlagen hattest und für jeden Typ eine eigene Load Prozedur geschrieben. Habs jetzt ausprobiert und funktioniert prima! Danke nochmal.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!