Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi A/V bei löschen von Element in Array (https://www.delphipraxis.net/153009-v-bei-loeschen-von-element-array.html)

WorstNightmare 16. Jul 2010 22:59

Delphi-Version: 2010

A/V bei löschen von Element in Array
 
Hallo,

ich habe eine eigene StringListen Klasse für Plugin-DLLs geschrieben, damit ich Classes nicht einbinden muss (und sie somit nicht so aufgebläht werden). Die Liste bietet außerdem einige Extra-Funktionen, z.B. einen Filter darauf anzuwenden, d.h. alle Elemente die Filter nicht enthalten werden gelöscht.

Delphi-Quellcode:
procedure TCSList.ApplyFilter(F: string);
var
  i: Integer;
begin
  for i := 0 to Count - 1 do
    if Pos(F, FArray[i]) = 0 then
      if i = Count - 1 then
        SetLength(FArray, Count - 1)
      else
      begin
        Move(FArray[i + 1], FArray[i], (Count - 1 - i) * 4);  // String = 4 Byte Pointer
        SetLength(FArray, Count - 1);
        ApplyFilter(F);   // Count geändert, Schleife neustarten
        Exit;
      end;
end;
Das ganze basiert auf einem array of string (FArray), Count gibt einfach nur Length(FArray) aus. Das Problem ist, dass nach einem Durchlauf der Speicher irgendwie korrumpiert wird, denn plötzlich tauchen im Array noch andere Strings die irgendwo in der DLL vorkommen auf und irgendwann ist es dann so kaputt, dass eine A/V geworfen wird.

Vielleicht mögen die String-Pointer es nicht rumgeschoben zu werden? Ich konnte den Fehler nicht finden :(

himitsu 17. Jul 2010 00:11

AW: A/V bei löschen von Element in Array
 
Wenn du die Stringreferenzen nicht korrekt behandelt, dann muß es ja probleme geben.

der String, welchen du "angelich" löschts ... da überschreibst du nur den "Zeiger", aber der String selber bleibt als Speicherleck zurück.

Und als Ausgleich hast du durch dein Move auch noch den letzen String im Array mit einer doppelten Referenz, welcher aber nicht in der Referenzzählung auftaucht, womit der dann schon freigegeben wird, aber dennoch in der Liste steht ... taj, beim nächsten Zugriff darauf, da muß es einfach knallen.


Mein Tipp: verwende eine echte Stringliste, statt des Arrays ... so sehr aufblähen tut soeine Liste auch nicht und du mußt nicht an der Referenzzählung rumfummeln.

Ansonten mußt du die Zeiger halt korrekt behandeln, also manuell auf nil, bzw. einen Leerstring setzen.

rollstuhlfahrer 17. Jul 2010 09:57

AW: A/V bei löschen von Element in Array
 
Zitat:

Zitat von WorstNightmare (Beitrag 1035776)
Delphi-Quellcode:
Move(FArray[i + 1], FArray[i], (Count - 1 - i) * 4);  // String = 4 Byte Pointer

Das kannst du SO nicht machen. Strings selbst sind ja Arrays. Und wenn du jetzt anfängst in deinem FArray rumzuschieben, bringst du die ganze Struktur durcheinander. Einfacher ist es einfach zu sagen
Delphi-Quellcode:
FArray[i] := FArray[i + 1]
. Dies hat den Vorteil, dass der Compiler auch weiß, was du machen willst und sich demnach richten kann.

Bernhard

WorstNightmare 17. Jul 2010 10:02

AW: A/V bei löschen von Element in Array
 
So, habe jetzt vor dem Move
Delphi-Quellcode:
FArray[i] := '';  // Zu löschenden freigeben
das hinzugefügt.
Jetzt läuft der Code ohne Probleme durch, aber wie kann ich die doppelte Referenz verhindern? Der letzte Pointer steht dann ja kurzfristig 2x in der Liste (bis SetLength die Liste verkleinert), aber wenn ich den hinteren auf leer setze betrifft das ja auch den vorletzten.

@rollstuhlfahrer:
Strings sind Zeiger auf Arrays, das "Array" selbst steht woanders.

rollstuhlfahrer 17. Jul 2010 10:11

AW: A/V bei löschen von Element in Array
 
Hab ich mir auch noch nie Gedanken gemacht. Ich hätte gleich die Delete()-Funktion der Stringliste aufgerufen. So hätte ich mit Denkaufwand und Redundanz gespart.

Bernhard

himitsu 17. Jul 2010 11:05

AW: A/V bei löschen von Element in Array
 
Delphi-Quellcode:
FArray[i] := '';
Move(FArray[i + 1], FArray[i], (Count - 1 - i) * SizeOf(Pointer));
Pointer(FArray[Count - 1]) := nil;
SetLength(FArray, Count - 1);
Aber, wie gesagt, nimm lieber eine Stringliste .. diese ist nur unwesentlich größer, der Overhead ist minimal und man erspart sich viel Arbeit und eventuelle Probleme und nur ein Delete(i) wäre auch verständlicher.

Und vorallem, wenn man nciht genau weiß was man macht und warum, ist es eh keine gute Idee Automatismen (wie die delphiinterne Speicherverwaltung/Referenzzählung) zu manipulieren.


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:52 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz