![]() |
Unterschiedliche Ergebnisse TList.Sort 32bit / 64bit CompareItems
Hallo zusammen,
ich stehe aktuell auf dem Schlauch. Die Sortierfunktion von TList gibt mir ein anderes Ergebnis bei einem 32bit Compilat wie bei einem 64bit Compilat. Könnt ihr mir sagen warum? Delphi 11, Windows 10 64bit
Delphi-Quellcode:
Ergebnis nach Sortierung 32bit:
unit Unit3;
interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Math; type PMyStruct = ^TMyStruct; TMyStruct = packed record Value1 : LongInt; Value2 : Double; Value3 : Byte; function GetDataStr: String; end; type TForm3 = class(TForm) Memo1: TMemo; Memo2: TMemo; procedure FormCreate(Sender: TObject); private { Private-Deklarationen } public { Public-Deklarationen } end; var Form3: TForm3; implementation {$R *.dfm} procedure TForm3.FormCreate(Sender: TObject); var aList : TList; aData : PMyStruct; i: Integer; function CompareItems(const Item1,Item2: PMyStruct): Integer; begin Result := Math.CompareValue(Item1^.Value1, Item2^.Value1); if Result <> 0 then exit; Result := Math.CompareValue(Item2^.Value2, Item1^.Value2); if Result <> 0 then exit; Result := Math.CompareValue(Item1^.Value3, Item2^.Value3); end; begin aList := TList.Create; try for i := 0 to 5 do begin New(aData); aData^.Value1 := 1429078; aData^.Value2 := 6.220; aData^.Value3 := i; aList.Add(aData); end; for i := 0 to 5 do begin New(aData); aData^.Value1 := 1429079; aData^.Value2 := 6.220; aData^.Value3 := i; aList.Add(aData); end; for i := 0 to 5 do begin New(aData); aData^.Value1 := 1429080; aData^.Value2 := 6.220; aData^.Value3 := i; aList.Add(aData); end; for i := 0 to 0 do begin New(aData); aData^.Value1 := 1429081; aData^.Value2 := 6.220; aData^.Value3 := i; aList.Add(aData); end; for i := 0 to 1 do begin New(aData); aData^.Value1 := 1429081; aData^.Value2 := 5.810; aData^.Value3 := i; aList.Add(aData); end; for i := 0 to 4 do begin New(aData); aData^.Value1 := 1429082; aData^.Value2 := 5.810; aData^.Value3 := i; aList.Add(aData); end; for i := 0 to 5 do begin New(aData); aData^.Value1 := 1429083; aData^.Value2 := 5.810; aData^.Value3 := i; aList.Add(aData); end; for i := 0 to 5 do begin New(aData); aData^.Value1 := 1429084; aData^.Value2 := 5.810; aData^.Value3 := i; aList.Add(aData); end; Memo1.Clear; Memo1.Lines.Add('VOR Sortierung'); for i := 0 to aList.Count - 1 do begin aData := aList.Items[i]; Memo1.Lines.Add('(' + i.ToString +') - aData: ' + aData^.GetDataStr); end; aList.Sort(@CompareItems); Memo2.Clear; Memo2.Lines.Add('NACH Sortierung'); for i := 0 to aList.Count - 1 do begin aData := aList.Items[i]; Memo2.Lines.Add('(' + i.ToString +') - aData: ' + aData^.GetDataStr); end; finally FreeAndNil(aList); end; end; { TMyStruct } function TMyStruct.GetDataStr: String; begin Result := Value1.ToString + '/' + FloatToStrF(Value2, ffFixed, 15, 3) + '/' + Value3.ToString; end; end. NACH Sortierung (0) - aData: 1429078/6,220/0 (1) - aData: 1429078/6,220/1 (2) - aData: 1429078/6,220/2 (3) - aData: 1429078/6,220/3 (4) - aData: 1429078/6,220/4 (5) - aData: 1429078/6,220/5 (6) - aData: 1429079/6,220/0 (7) - aData: 1429079/6,220/1 (8) - aData: 1429079/6,220/2 (9) - aData: 1429079/6,220/3 (10) - aData: 1429079/6,220/4 (11) - aData: 1429079/6,220/5 (12) - aData: 1429080/6,220/0 (13) - aData: 1429080/6,220/1 (14) - aData: 1429080/6,220/2 (15) - aData: 1429080/6,220/3 (16) - aData: 1429080/6,220/4 (17) - aData: 1429080/6,220/5 (18) - aData: 1429081/6,220/0 (19) - aData: 1429081/5,810/0 (20) - aData: 1429081/5,810/1 (21) - aData: 1429082/5,810/0 (22) - aData: 1429082/5,810/1 (23) - aData: 1429082/5,810/2 (24) - aData: 1429082/5,810/3 (25) - aData: 1429082/5,810/4 (26) - aData: 1429083/5,810/0 (27) - aData: 1429083/5,810/1 (28) - aData: 1429083/5,810/2 (29) - aData: 1429083/5,810/3 (30) - aData: 1429083/5,810/4 (31) - aData: 1429083/5,810/5 (32) - aData: 1429084/5,810/0 (33) - aData: 1429084/5,810/1 (34) - aData: 1429084/5,810/2 (35) - aData: 1429084/5,810/3 (36) - aData: 1429084/5,810/4 (37) - aData: 1429084/5,810/5 Ergebnis nach Sortierung 64bit: NACH Sortierung (0) - aData: 1429082/5,810/3 (1) - aData: 1429082/5,810/4 (2) - aData: 1429083/5,810/0 (3) - aData: 1429083/5,810/1 (4) - aData: 1429082/5,810/2 (5) - aData: 1429081/5,810/0 (6) - aData: 1429081/5,810/1 (7) - aData: 1429082/5,810/0 (8) - aData: 1429082/5,810/1 (9) - aData: 1429083/5,810/2 (10) - aData: 1429084/5,810/2 (11) - aData: 1429084/5,810/3 (12) - aData: 1429084/5,810/4 (13) - aData: 1429084/5,810/5 (14) - aData: 1429084/5,810/1 (15) - aData: 1429083/5,810/3 (16) - aData: 1429083/5,810/4 (17) - aData: 1429083/5,810/5 (18) - aData: 1429084/5,810/0 (19) - aData: 1429078/6,220/5 (20) - aData: 1429079/6,220/0 (21) - aData: 1429079/6,220/1 (22) - aData: 1429079/6,220/2 (23) - aData: 1429078/6,220/4 (24) - aData: 1429078/6,220/0 (25) - aData: 1429078/6,220/1 (26) - aData: 1429078/6,220/2 (27) - aData: 1429078/6,220/3 (28) - aData: 1429079/6,220/3 (29) - aData: 1429080/6,220/3 (30) - aData: 1429080/6,220/4 (31) - aData: 1429080/6,220/5 (32) - aData: 1429081/6,220/0 (33) - aData: 1429080/6,220/2 (34) - aData: 1429079/6,220/4 (35) - aData: 1429079/6,220/5 (36) - aData: 1429080/6,220/0 (37) - aData: 1429080/6,220/1 Anmerkung: Erste Durchlauf von "Result := Math.CompareValue(Item1^.Value1, Item2^.Value1);" unter 32bit ist Item1^.Value1 -> 1429078 und Item2^.Value1 -> 1429081 Erste Durchlauf von "Result := Math.CompareValue(Item1^.Value1, Item2^.Value1);" unter 64bit ist Item1^.Value1 -> 1429081 und Item2^.Value1 -> 1429081 Das heißt, die Liste selber ruft meine Comparefunktion schon unterschiedlich auf... |
AW: Unterschiedliche Ergebnisse TList.Sort 32bit / 64bit CompareItems
Du kannst eine lokale Funktion nicht als Pointer übergeben. Das geht spätestens bei 64-Bit wegen der anderen Aufrufkonventionen schief. Das @ beim Sort ist schon ein markanter Hinweis für eine Fehlerursache.
|
AW: Unterschiedliche Ergebnisse TList.Sort 32bit / 64bit CompareItems
Liste der Anhänge anzeigen (Anzahl: 1)
ok, wie muss es dann heißen?
Selbst in der Hilfe steht es doch so... Woher soll man das dann wissen :roll: |
AW: Unterschiedliche Ergebnisse TList.Sort 32bit / 64bit CompareItems
Wahrscheinlich klappt es, wenn Du die function CompareItems(..) direkt in TForm3 = class(TForm) verschiebst.
|
AW: Unterschiedliche Ergebnisse TList.Sort 32bit / 64bit CompareItems
Entweder so:
Delphi-Quellcode:
Oder gleich mit einer generischen Liste (Das spart übrigens auch die Freigabe der Records):
type
PMyStruct = ^TMyStruct; TMyStruct = packed record Value1 : LongInt; Value2 : Double; Value3 : Byte; class function Compare(const A, B: TMyStruct): TValueRelationship; static; function GetDataStr: String; end; class function TMyStruct.Compare(const A, B: TMyStruct): TValueRelationship; begin Result := CompareValue(A.Value1, B.Value1); if Result <> 0 then exit; Result := CompareValue(A.Value2, B.Value2); if Result <> 0 then exit; Result := CompareValue(A.Value3, B.Value3); end; function CompareItems(PItem1,PItem2: Pointer): Integer; begin Result := TMyStruct.Compare(PMyStruct(PItem1)^, PMyStruct(PItem2)^); end; ... aList.Sort(CompareItems); ...
Delphi-Quellcode:
type
TMyStruct = packed record Value1 : LongInt; Value2 : Double; Value3 : Byte; constructor Create(const AValue1: LongInt; const AValue2: Double; const AValue3: Byte); class function Compare(const A, B: TMyStruct): Integer; static; function GetDataStr: String; end; TMyStructList = class(TList<TMyStruct>) public constructor Create; end; constructor TMyStruct.Create(const AValue1: LongInt; const AValue2: Double; const AValue3: Byte); begin Value1 := AValue1; Value2 := AValue2; Value3 := AValue3; end; class function TMyStruct.Compare(const A, B: TMyStruct): Integer; begin Result := CompareValue(A.Value1, B.Value1); if Result <> 0 then exit; Result := CompareValue(A.Value2, B.Value2); if Result <> 0 then exit; Result := CompareValue(A.Value3, B.Value3); end; function TMyStruct.GetDataStr: String; begin Result := Value1.ToString + '/' + FloatToStrF(Value2, ffFixed, 15, 3) + '/' + Value3.ToString; end; constructor TMyStructList.Create; begin inherited Create(TComparer<TMyStruct>.Construct(TMyStruct.Compare)); end; ... var aList := TMyStructList.Create; try for var i := 0 to 5 do aList.Add(TMyStruct.Create(1429078, 6.220, i)); for var i := 0 to 5 do aList.Add(TMyStruct.Create(1429079,6.220, i)); for var i := 0 to 5 do aList.Add(TMyStruct.Create(1429080,6.220, i)); for var i := 0 to 0 do aList.Add(TMyStruct.Create(1429081,6.220, i)); for var i := 0 to 1 do aList.Add(TMyStruct.Create(1429081,5.810, i)); for var i := 0 to 4 do aList.Add(TMyStruct.Create(1429082,5.810, i)); for var i := 0 to 5 do aList.Add(TMyStruct.Create(1429083,5.810, i)); for var i := 0 to 5 do aList.Add(TMyStruct.Create(1429084,5.810, i)); Memo1.Clear; Memo1.Lines.Add('VOR Sortierung'); for var i := 0 to aList.Count - 1 do begin var aData := aList.Items[i]; Memo1.Lines.Add('(' + i.ToString +') - aData: ' + aData.GetDataStr); end; aList.Sort; Memo2.Clear; Memo2.Lines.Add('NACH Sortierung'); for var i := 0 to aList.Count - 1 do begin var aData := aList.Items[i]; Memo2.Lines.Add('(' + i.ToString +') - aData: ' + aData.GetDataStr); end; finally FreeAndNil(aList); end; ... |
AW: Unterschiedliche Ergebnisse TList.Sort 32bit / 64bit CompareItems
Danke Uwe, habe Variante 1 genommen.
Auf Generic konnte ich nicht so leicht umbauen aber Variante 1 funktioniert. Danke. Was lernen wir daraus? Nicht auf die Hilfe hören :-D |
AW: Unterschiedliche Ergebnisse TList.Sort 32bit / 64bit CompareItems
Zitat:
|
AW: Unterschiedliche Ergebnisse TList.Sort 32bit / 64bit CompareItems
Mir war so, als ob ich dieses Problem schonmal gesehen habe und jupp,
![]() Zitat:
Habs mal ![]() |
AW: Unterschiedliche Ergebnisse TList.Sort 32bit / 64bit CompareItems
Methoden und diese Inline, sowie die Generischen haben einen "unsichtbaren" ersten Self-Parameter.
Somit stimmt die Signatur dann nicht, im Vergleich mit einer "reinen" Prozedur. Bei Methoden und Klassenmethoden kann man das mit Static beheben, aber für Untermethoden leider nicht. |
AW: Unterschiedliche Ergebnisse TList.Sort 32bit / 64bit CompareItems
Zitat:
Dieser hat aber nix mit dem Self-Parameter wie bei Methoden (entweder Referenz zur Instanz oder zu Klasse) zu tun, sondern hier wird die Addresse zum umgebenden Stackframe übergeben. Darüber wird der Zugriff auf die Variablen außerhalb der Routine realisiert. Wie David auf die von mir verlinkten Stackoverflow richtig erklärt, gibt es diesen unter Win64 immer, egal ob man auf den umgebenden Scope zugreift oder nicht. Unter Win32 gibt es diesen nur wenn man auf den umgebenden Scope zugreift - deshalb funktioniert es dort. Unter Win64 werden die ersten vier Parameter in den Registern rcx, rdx, r8 und r9 übergeben. Also sollten Item1 und Item2 in rcx und rdx sein, da wirds auch durch den Aufruf aus Sort heraus übergeben. Dadurch, dass die Implementierung aber nested ist, nimmt der Compiler aber an, dass diese in rdx und r8 sind, denn in rcx erwartet er den Stackzeiger, wenn die Routine lokal aufgerufen wird. Das kann man sehr einfach auch überprüfen, wenn man einfach mal in das FormCreate
Delphi-Quellcode:
reinschreibt, dort einen Stoppunkt setzt und sich das Disassembly anschaut:
CompareItems(nil, nil);
Delphi-Quellcode:
Wie man sieht, wird rbp (Frame pointer) in rcx übergeben, wie ich zuvor beschrieben habe. Verschiebe ich CompareItems nach außerhalb schauts so aus:
mov rcx,rbp
xor edx,edx xor r8,r8 call CompareItems
Delphi-Quellcode:
xor ecx,ecx
xor edx,edx call CompareItems |
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:17 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