AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Programmieren allgemein Eintrag aus Array löschen funktioniert bei 64 bit nicht
Thema durchsuchen
Ansicht
Themen-Optionen

Eintrag aus Array löschen funktioniert bei 64 bit nicht

Ein Thema von DieDolly · begonnen am 9. Feb 2019 · letzter Beitrag vom 13. Feb 2019
Antwort Antwort
DieDolly

Registriert seit: 22. Jun 2018
2.175 Beiträge
 
#1

Eintrag aus Array löschen funktioniert bei 64 bit nicht

  Alt 9. Feb 2019, 18:34
Ich benutze seit einer Ewigkeit diesen Codeschnipsel und er funktioniert mit x86.
Mit x64 funktioniert er genau 2x und dann bekomme ich eine ungültige Zeigeroperation. Der Input ist immer der gleiche. Ein String mit mehreren Teilen geteilt durch |, die ich mit Explode in ein Array packe. Das klappt alles bis ich den ersten Eintrag löschen möchte. Dann kommt der Fehler.

Delphi-Quellcode:
procedure DeleteArrayIndex(var AArray: TArray<string>; AIndex: Integer);
begin
 if AIndex > High(AArray) then
  Exit;
 if AIndex < Low(AArray) then
  Exit;
 if AIndex = High(AArray) then
  begin
   SetLength(AArray, Length(AArray) - 1);
   Exit;
  end;
 Finalize(AArray[AIndex]);
 System.Move(AArray[AIndex + 1], AArray[AIndex], (Length(AArray) - AIndex - 1) * SizeOf(string) + 1);
 SetLength(AArray, Length(AArray) - 1);
end;

if StringParts[0] = 'HEADERthen
 DeleteArrayIndex(StringParts, 0); // Fehler
Das Array ist immer mindestens 3 Einträge lang.
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe
Online

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.475 Beiträge
 
Delphi 12 Athens
 
#2

AW: Eintrag aus Array löschen funktioniert bei 64 bit nicht

  Alt 9. Feb 2019, 18:39
Wofür ist denn das + 1 am Ende?

System.Move(AArray[AIndex + 1], AArray[AIndex], (Length(AArray) - AIndex - 1) * SizeOf(string) + 1); Welche Delphi Version?
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
DieDolly

Registriert seit: 22. Jun 2018
2.175 Beiträge
 
#3

AW: Eintrag aus Array löschen funktioniert bei 64 bit nicht

  Alt 9. Feb 2019, 18:52
Rio. Der Code wurde einfach von irgendwoher vor Jahren mal kopiert.
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe
Online

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.475 Beiträge
 
Delphi 12 Athens
 
#4

AW: Eintrag aus Array löschen funktioniert bei 64 bit nicht

  Alt 9. Feb 2019, 23:41
Der Code ist meiner Meinung nach falsch - eventuell nur falsch abgeschrieben.

Das Move erwartet im dritten Parameter die Anzahl Bytes. Hier wird aus unerfindlichen Gründen 1 Byte zuviel angegeben. Der korrekte Code müsste so lauten:

System.Move(AArray[AIndex + 1], AArray[AIndex], (Length(AArray) - AIndex - 1) * SizeOf(string));

Einfacher geht es aber mit

Delete(AArray, AIndex, 1);

Siehe: System.Delete
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Benutzerbild von p80286
p80286

Registriert seit: 28. Apr 2008
Ort: Stolberg (Rhl)
6.659 Beiträge
 
FreePascal / Lazarus
 
#5

AW: Eintrag aus Array löschen funktioniert bei 64 bit nicht

  Alt 10. Feb 2019, 09:38
Ich vermute, er möchte noch eine trailing 0 mitschleppen. Das war vielleicht mal sinnvoll......

Gruß
K-H
Programme gehorchen nicht Deinen Absichten sondern Deinen Anweisungen
R.E.D retired error detector
  Mit Zitat antworten Zitat
HolgerX

Registriert seit: 10. Apr 2006
Ort: Leverkusen
972 Beiträge
 
Delphi 6 Professional
 
#6

AW: Eintrag aus Array löschen funktioniert bei 64 bit nicht

  Alt 10. Feb 2019, 10:20
Hmm..


Ich vermute, er möchte noch eine trailing 0 mitschleppen. Das war vielleicht mal sinnvoll......

Gruß
K-H
Aber auch dann währe es falsch, da hier das Byte (versucht) kopiert wird, welches sich 'hinter' dem Array befindet.
Und dies kann irgendetwas sein.

Auch schreibt er dieses Byte dann 'hinter' das Array in einen Speicher, welcher nicht zum Array gehört und dann kann es knallen..

(Edit: OK, knallen würde es hier nicht, da er das Array erst danach abschneidet.. Aber generell sollte mit Move vorsichtig umgegangen werden )
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe
Online

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.475 Beiträge
 
Delphi 12 Athens
 
#7

AW: Eintrag aus Array löschen funktioniert bei 64 bit nicht

  Alt 10. Feb 2019, 10:58
Ich vermute, er möchte noch eine trailing 0 mitschleppen. Das war vielleicht mal sinnvoll......
Das kann nicht mal zu Zeiten von string = string[255] funktioniert haben. Ein TArray<string> ist immer ein Array von Pointern und Sizeof(string) ist immer 4 (32 Bit) oder 8 (64 Bit). Das 1 Byte mehr macht hier definitiv nur Ärger. Es sorgt nämlich dafür, daß der letzte Array-Eintrag verfälscht wird.

Ich wäre in diesem Fall aber sowieso vorsichtig, was die Referenzzählung bei Strings betrifft. Die kann mit dem Move schon mal gehörig durcheinander geraten. Selbst bei korrektem Count zeigen nach dem Move die letzten beiden Array-Elemente auf denselben String. Ich glaube kaum, daß bei dem Move auch die Referenzzählung entsprechend angepasst wird. Beim folgenden SetLength allerdings schon. Dieses Beispielprogramm verdeutlicht die Problematik und zeigt, daß die Verwendung von Delete nicht nur einfacher, sondern auch sicherer ist.


Delphi-Quellcode:
program Project490;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils;

function StringRefCount(const Value: string): Integer;
var
  P: PInteger;
begin
  P := @Value[1];
  Dec(P, 2);
  Result := P^;
end;

procedure Main;
var
  arr: TArray<string>;
  I: Integer;
begin
  SetLength(arr, 10);
  for I := 0 to 8 do begin
    arr[I] := I.ToString;
  end;
  arr[9] := arr[8];

  Writeln('Verwendung von Move und SetLength');
  Writeln('8 und 9 zeigen auf denselben string, RefCount ist OK');
  Writeln('7: ', StringRefCount(arr[7]));
  Writeln('8: ', StringRefCount(arr[8]));
  Writeln('9: ', StringRefCount(arr[9]));
  Move(arr[1], arr[0], 9*SizeOf(arr[0]));
  Writeln('nach Move: 7,8 und 9 zeigen auf denselben string, aber RefCount is 2!');
  Writeln('7: ', StringRefCount(arr[7]));
  Writeln('8: ', StringRefCount(arr[8]));
  Writeln('9: ', StringRefCount(arr[9]));
  SetLength(arr, 9);
  Writeln('nach SetLength: RefCount für 9 wird runtergezählt.');
  Writeln('7 und 8 zeigen auf denselben string, aber RefCount ist 1!');
  Writeln('7: ', StringRefCount(arr[7]));
  Writeln('8: ', StringRefCount(arr[8]));
  arr[8] := '';
  Writeln('8 ist jetzt leer, RefCount wurde erniedrigt.');
  Writeln('7 zeigt immer noch auf diesen string, aber RefCount ist 0!');
  Writeln('7: ', StringRefCount(arr[7]));
  Writeln('8: ', StringRefCount(arr[8]));
  Writeln;
end;

procedure Main2;
var
  arr: TArray<string>;
  I: Integer;
begin
  SetLength(arr, 10);
  for I := 0 to 8 do begin
    arr[I] := I.ToString;
  end;
  arr[9] := arr[8];

  Writeln('Verwendung von Delete');
  Writeln('8 und 9 zeigen auf denselben string, RefCount ist OK');
  Writeln('7: ', StringRefCount(arr[7]));
  Writeln('8: ', StringRefCount(arr[8]));
  Writeln('9: ', StringRefCount(arr[9]));
  Delete(arr, 0, 1);
  Writeln('nach Delete: RefCount für 9 wird runtergezählt.');
  Writeln('7 und 8 zeigen auf denselben string, RefCount ist OK');
  Writeln('7: ', StringRefCount(arr[7]));
  Writeln('8: ', StringRefCount(arr[8]));
  arr[8] := '';
  Writeln('8 ist jetzt leer, RefCount wurde erniedrigt.');
  Writeln('7 zeigt immer noch auf diesen string, RefCount ist OK');
  Writeln('7: ', StringRefCount(arr[7]));
  Writeln('8: ', StringRefCount(arr[8]));
  Writeln;
end;

begin
  try
    Main;
    Main2;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Blup

Registriert seit: 7. Aug 2008
Ort: Brandenburg
1.477 Beiträge
 
Delphi 12 Athens
 
#8

AW: Eintrag aus Array löschen funktioniert bei 64 bit nicht

  Alt 13. Feb 2019, 10:56
So würde es funktionieren:
Delphi-Quellcode:
 Finalize(AArray[AIndex]);
 System.Move(AArray[AIndex + 1], AArray[AIndex], (Length(AArray) - AIndex - 1) * SizeOf(string));
 {letzten Eintrag auf nil setzen, damit keine Referenzzählung für den jetzt doppelten Eintrag bei SetLength erfolgt}
 System.FillChar(AArray[Length(AArray) - 1], SizeOf(string), 0); // oder Pointer(AArray[Length(AArray) - 1]) := nil;
 SetLength(AArray, Length(AArray) - 1);
Inzwischen ist das wie beschrieben einfacher zu lösen:
Delphi-Quellcode:
System.Delete(AArray, AIndex, 1);
.
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 23:13 Uhr.
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