Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Zugriffsverletzung beim Kürzen eines dyn. Arrays (https://www.delphipraxis.net/165992-zugriffsverletzung-beim-kuerzen-eines-dyn-arrays.html)

roadrunner-S51 25. Jan 2012 10:27

Zugriffsverletzung beim Kürzen eines dyn. Arrays
 
Hallo,

ich habe mal wieder einen interessanten Fehler, bei dem ich mir eure Hilfe erhoffe.

Beschreibung:
In einem Programm werden Daten aus einem vorher erstellen dyn. Array ausgelesen. Ist das Auslesen abgeschlossen soll die entsprechende Zeile im dyn. Array gelöscht und das Array somit um ein Element gekürzt werden. Das klappt auch ganz gut, bis ich zum letzen Element (logischerweise Element 0) komme. Dann erhalte ich eine Zugriffsverletzung mit variablen Adressen.
Das kürzendes Feldes erfolgt in einer Prozedur. Wenn ich die aus meinem Gesamtcode rausnehme läuft alles Problemlos, daher vermute ich den Fehler dort.

Code der Prozedur:
Delphi-Quellcode:
procedure Loesche_Array_Element(const AIndex: Integer);
begin
  if Length(Feldplatz) = 1 then
  begin
    SetLength(Feldplatz, 0); // Länge kürzen
    //Feldplatz := nil;
  end
  else
  begin
    Move(Feldplatz[AIndex + 1], Feldplatz[AIndex], SizeOf(Feldplatz[0]) * (Length(Feldplatz) - AIndex - 1)); //Dahinterliegende Daten aufrücken
    SetLength(Feldplatz, Length(Feldplatz) - 1); // Länge kürzen
  end;
end;
Ich hoffe es hat jemand eine Idee, wie ich das Problem lösen kann. Danke schonmal!!!

uligerhardt 25. Jan 2012 10:38

AW: Zugriffsverletzung beim Kürzen eines dyn. Arrays
 
Was ist denn der Typ der Array-Elemente? Wenn da z.B. Strings drin sind, wird das mit dem Move so nicht funktionieren, weil du die Referenzzählung torpedierst.

Klaus01 25. Jan 2012 10:46

AW: Zugriffsverletzung beim Kürzen eines dyn. Arrays
 
Hallo,

auch wenn dies nicht Deine Frage beantwortet..

Warum benutzt Du nicht die schon vorhanden Listen wie TStringList, TList oder TObjectList dor kannst Du problemlos etwas herauslöschen ohne das Du Dich darum
kümmern musst die folgenden Elemente nachzurücken.

Grüße
Klaus

roadrunner-S51 25. Jan 2012 10:48

AW: Zugriffsverletzung beim Kürzen eines dyn. Arrays
 
Das Array ist folgendermaßen aufgebaut:

Delphi-Quellcode:
  Sortierung = Record
  iRegalPlatz  : Integer;        // Regalplatz-Nr.
  iPosiPlatz   : Integer;        // Kistenplatz-Nr. im Regalplatz
  iKistePlatz  : Integer;        // Kisten-Nr.
  iStueckPlatz : Integer;        // Stückzahl in der Kiste
  Datum        : TDateTime;      // Einlagerungsdatum
  Barcode      : String;         // Inhalt des Strings des Barcodes
  end;

  FeldPlatz  : Array of Sortierung;
Also funktionieren tut es insgesamt schon. Nur eben wenn AIndex 0 ist, bzw. das Feld die Länge 1 hat, tritt der Fehler auf.

@Klaus01: Das ist an sich eine Idee. Aber in diesem Zusammenhang und bei dem Umfang des Programms macht sich das nicht so gut.

Luckie 25. Jan 2012 10:56

AW: Zugriffsverletzung beim Kürzen eines dyn. Arrays
 
Zitat:

Zitat von roadrunner-S51 (Beitrag 1147496)
@Klaus01: Das ist an sich eine Idee. Aber in diesem Zusammenhang und bei dem Umfang des Programms macht sich das nicht so gut.

Das musst du mir jetzt aber mal begründen. Es reduziert den Code. Es macht den Code stabiler. Du kannst dich auf das hauptsächliche Problem konzentrieren.

uligerhardt 25. Jan 2012 11:01

AW: Zugriffsverletzung beim Kürzen eines dyn. Arrays
 
Zitat:

Zitat von roadrunner-S51 (Beitrag 1147496)
Das Array ist folgendermaßen aufgebaut:

Delphi-Quellcode:
  Sortierung = Record
  iRegalPlatz  : Integer;        // Regalplatz-Nr.
  iPosiPlatz   : Integer;        // Kistenplatz-Nr. im Regalplatz
  iKistePlatz  : Integer;        // Kisten-Nr.
  iStueckPlatz : Integer;        // Stückzahl in der Kiste
  Datum        : TDateTime;      // Einlagerungsdatum
  Barcode      : String;         // Inhalt des Strings des Barcodes
  end;

  FeldPlatz  : Array of Sortierung;
Also funktionieren tut es insgesamt schon. Nur eben wenn AIndex 0 ist, bzw. das Feld die Länge 1 hat, tritt der Fehler auf.

Und du hast nicht $H-/$LONGSTRINGS OFF, d.h. Barcode ist ein referenzgezählter Typ? Dann verursacht das Move Probleme, selbst wenn du sie nicht sofort um die Ohren geknallt kriegst. Überleg mal: Angenommen Feldplatz hat noch zwei Elemente und du rufst Loesche_Array_Element(0) auf. Dann wird der Record Feldplatz[1] auf Feldplatz[0] ge-move-t, d.h. Feldplatz[0].Barcode und Feldplatz[1].Barcode verweisen auf den gleichen String, haben aber trotzdem nur Referenzzähler 1. Beim nachfolgenden Aufruf von SetLength wird Feldplatz[1] finalisiert, was insbesondere den Referenzzähler von Feldplatz[1].Barcode dekrementiert und, da 0 rauskommt, den String entsorgt. Dummerweise zeigt Feldplatz[0].Barcode jetzt auf einen freigegebenen Speicherbereich. Das kracht nicht zwangsweise sofort, ist aber trotzdem übel. :mrgreen:

himitsu 25. Jan 2012 11:02

AW: Zugriffsverletzung beim Kürzen eines dyn. Arrays
 
Zitat:

Delphi-Quellcode:
String

Bei diesen Typen verwaltet Delphi den Speicherplatz.
Mit deiner "unsicheren" Umkopieroperation (und ohne korrekte Behandlung dieser Variablen/Typen) zerschießt du natürlich die Speicherverwaltung.

Statt Delphi-Referenz durchsuchenString könntest du einen Delphi-Referenz durchsuchenShortString (Länge 255), bzw. einen ShortString mit Längenangabe
Delphi-Quellcode:
String[123]
verwenden.

Außerdem sind Interfaces, Variants und andere dynamische Arrays ebenfalls verboten.

roadrunner-S51 25. Jan 2012 11:43

AW: Zugriffsverletzung beim Kürzen eines dyn. Arrays
 
Ich danke für die Hilfe!

Kurzzeitgi hab ich das Problem jetzt erstmal so gelöst, dass ich bevor ich das Element lösche prüfe, ob das Feld nurnoch ein Element hat. Wenn das der Fall ist, dann unterlasse ich den Aufruf der Prozedur.

Längerfristig gesehen werde ich den Code im Ganzen nochmal überarbeiten müssen.

@Luckie: Mit einer "Begründung" kann ich zwar nicht dienen aber mit meiner persönlichen Einschätzung. :wink:
Bisher war ich der Meinung, in der Stringlist wie der Name sagt, lediglich Strings handeln zu können. Dies würde bedeuten, dass ich alle Werte die ich in dem Record habe in Strings wandeln müsste und ggf. zurück. Dies ist bei der Vielzahl von Abfragen und Handlingfunktionen im Programm sehr aufwendig und unübersichtlich.

Klaus01 25. Jan 2012 11:50

AW: Zugriffsverletzung beim Kürzen eines dyn. Arrays
 
Zitat:

Zitat von roadrunner-S51 (Beitrag 1147518)
@Luckie: Mit einer "Begründung" kann ich zwar nicht dienen aber mit meiner persönlichen Einschätzung. :wink:
Bisher war ich der Meinung, in der Stringlist wie der Name sagt, lediglich Strings handeln zu können. Dies würde bedeuten, dass ich alle Werte die ich in dem Record habe in Strings wandeln müsste und ggf. zurück. Dies ist bei der Vielzahl von Abfragen und Handlingfunktionen im Programm sehr aufwendig und unübersichtlich.

.. nun man muss ja für Records nicht unbedingt eine StringListe hernehmen.
Ich weiß nicht wie gut in Delphi 2009 schon die Generics sind..

Wenn könnte man auch generische Listen verwenden.

Delphi-Quellcode:
TFeldplatz = TList<TSortierung>;
Grüße
Klaus

himitsu 25. Jan 2012 11:53

AW: Zugriffsverletzung beim Kürzen eines dyn. Arrays
 
Wobei die Zugriffsverletzung nicht wegen dem gelöschten Element geschieht.
Dort baust du dir (so, wie du es machst) nur ein Speicherleck ein.

Die Zugriffsverletzung kommt von dem letzen Element, in dem Array, denn das gibt es plötzlich zwei Mal.
(Eines gibst du frei, beim kürzen des Arrays, und die vorhandene Kopie wird damit geschrottet)


Vorm Verschieben müßte man das zu löschende Element freigeben (Finalize) dann verschieben, den letzen Eintrag (beim Löschen ... beim Einfügen der Erste an der Einfügestelle) nullen und danach die Arraygröße anpassen.


Am Sichersten:
- über eine Schleife jeden Eintrag einzeln vorziehen (den Record von Delphi kopieren lassen) und dann die Größe anpassen

Oder man tauscht (mit binären Kopieroperationen, über einen "einfachen" Puffer, wie z.B. ein ByteArray) die Einträge aus.
- zu Löschenden zwischenspeichern
- verschieben
- den letzen Eintrag überschreiben, mit dem Gespeicherten (ebenfall binär kopiert)
- Arraygröße anpassen

oder
- zu Löschenden und letzen Eintrag austauschen (Idealer Weise über einen Zwischenspeicher vom selben Typen ... Delphi kopiert also)
- Arraygröße anpassen


Alle Zeitangaben in WEZ +1. Es ist jetzt 22:44 Uhr.
Seite 1 von 2  1 2      

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