Autor |
Beitrag |
Knulli
Beiträge: 116
Erhaltene Danke: 2
Win2k, Win7, Win10
D5, D2005, D2006, D2007, D10.4.2
|
Verfasst: Mi 20.03.19 18:30
Hi Leute,
ich möchte gerne die Format-Funktion mit einem dynamischen Array als Argumentenliste aufrufen.
--> Format(stFmt, Args)
Den FormatString und das Array für die Argumente möchte ich mir in Abhängigkeit diverser Bedingungen selber zusammenbasteln.
Leider zeschießt sich das Array beim dynamischen längermachen von selbst.
Ab dem vierten Eintrag wird der LastIDX-2 mit überschrieben.
Was mache ich falsch?
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33:
| procedure TForm1.Button1Click(Sender: TObject); type TMyVarRecArray = Array of TVarRec; var stFMT: String; stResult: String; Args: TMyVarRecArray; Loop: Integer; procedure AddToVarRecArray(var aVarRecArray: TMyVarRecArray; aValues: Array of const); var VarRec: TVarRec; begin for VarRec in aValues do begin SetLength(aVarRecArray, Length(aVarRecArray) + 1); Args[High(aVarRecArray)] := VarRec; end; end; begin
stFmt := 'Das wird eine Zeile: '; SetLength(Args, 0); for Loop := 1 to 5 do begin stFmt := stFmt + ' %s '; AddToVarRecArray(Args, ['Wert ' + IntToStr(Loop)]); end; stResult := Format(stFmt, Args); MessageBox(0, PChar(stResult), 'NaNu', MB_OK); end; |
Knulli
_________________ Echte Männer schreiben Windows-Programme in Assembler unter edlin.
|
|
GuaAck
Beiträge: 378
Erhaltene Danke: 32
Windows 8.1
Delphi 10.4 Comm. Edition
|
Verfasst: Mi 20.03.19 19:18
Hallo,
diese Zeile verstehe ich nicht und mein Compiler will si auch nicht übersetzen:
Delphi-Quelltext 1:
| for VarRec in aValues do |
Gruß
GuaAck
|
|
jasocul
Beiträge: 6388
Erhaltene Danke: 146
Windows 7 + Windows 10
Sydney Prof + CE
|
Verfasst: Do 21.03.19 08:25
In Delphi 7 gibt es diese Möglichkeit noch nicht.
Daher meckert dein Compiler
Zum eigentlichen Problem:
VarRec ist ein Zeiger auf einen temporären Speicher, der in der Schleife immer wieder genutzt wird.
Durch die Vergrößerung deines Records reservierst du zwar neuen Speicher, aber weist dann doch einen anderen (den oben genannten temporären) Speicherbereich zu.
Dadurch, dass dieser Speicher immer wieder benutzt wird, bekommst du deine falschen Werte.
Das kann soweit führen, dass du im späteren Programmverlauf immer wieder andere Informationen in deinem Array stehen hast, weil der Speicher durch andere Daten überschrieben werden kann. Abgesehen davon dürftest du dadurch auch ein Memoryleak bekommen.
Du musst also bei den Zuweisung in dein Array die Inhalte und nicht die Zeiger zuweisen.
In TVarRec stehen unter VType auch Typ-Informationen. Bei AnsiStrings (und nicht nur da) gibt es noch das zusätzliche Problem, dass die auch nur als Zeiger im Record stehen. Auch dieser Speicherbereich ist nur temporär und wird innerhalb von Delphi sicher wieder mit anderen Informationen während des Programm gefüllt werden können.
Für diesen Beitrag haben gedankt: Knulli
|
|
Knulli
Beiträge: 116
Erhaltene Danke: 2
Win2k, Win7, Win10
D5, D2005, D2006, D2007, D10.4.2
|
Verfasst: Do 21.03.19 15:12
OK, habs (hoffentlich) begiffen:
In TVarRec werden nur Referenzen gespeichert. Und weil bei meinem Aufruf von AddToVarRecArray das Argument nur auf dem Stack existiert (also innerhalb von AddToVarRecArray), gabs Datensalat bei den nachfolgenden Aufrufen.
Ich muss also bis zum Ende der "Nutzzeit" von Args ( also bis nach nach Format(...) ) den Speicherplatz für die Argumente vorhalten. Richtig verstanden?
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37:
| procedure TForm1.Button1Click(Sender: TObject); type TMyVarRecArray = Array of TVarRec; var stFMT: String; stResult: String; Args: TMyVarRecArray; Strings: Array of String; Loop: Integer; procedure AddToVarRecArray(var aVarRecArray: TMyVarRecArray; aValues: Array of const); var VarRec: TVarRec; begin for VarRec in aValues do begin SetLength(aVarRecArray, Length(aVarRecArray) + 1); Args[High(aVarRecArray)] := VarRec; end; end; begin
stFmt := 'Das wird eine Zeile: '; SetLength(Args, 0); for Loop := 1 to 5 do begin stFmt := stFmt + ' %s '; SetLength(Strings, Length(Strings) + 1); Strings[High(Strings)] := 'Wert ' + IntToStr(Loop); AddToVarRecArray(Args, [Strings[High(Strings)]]); end; stResult := Format(stFmt, Args); SetLength(Strings, 0); MessageBox(0, PChar(stResult), 'Hubba Hubba!!', MB_OK); end; |
Also kann ich das TVarRecArray nicht WIRKLICH effektiv nutzen.
Frisst Format noch andere Arrays / Listen, die sich auch die WERTE statt nur Referenzen merken können?
Knulli
_________________ Echte Männer schreiben Windows-Programme in Assembler unter edlin.
|
|
jasocul
Beiträge: 6388
Erhaltene Danke: 146
Windows 7 + Windows 10
Sydney Prof + CE
|
Verfasst: Fr 22.03.19 08:57
Knulli hat folgendes geschrieben : |
Also kann ich das TVarRecArray nicht WIRKLICH effektiv nutzen.
|
Es ist zumindest nicht trivial.
Ich habe nochmal etwas recherchiert, weil ich zu wenig Zeit habe, eine Lösung für dich zu erarbeiten, die brauchbar ist. Dabei habe ich folgendes gefunden:
https://stackoverflow.com/questions/6058697/how-to-set-string-or-ansistring-constant-in-the-tvarrec
Insbesondere die letzte Antwort beschreibt einen Lösungsansatz, der dein Problem lösen kann. Ist aber dennoch etwas Aufwand.
Knulli hat folgendes geschrieben : |
Frisst Format noch andere Arrays / Listen, die sich auch die WERTE statt nur Referenzen merken können? |
Format habe ich bisher noch nie genutzt. Da muss vielleicht mal ein anderer User was zu sagen.
|
|
jaenicke
Beiträge: 19285
Erhaltene Danke: 1743
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Fr 22.03.19 10:04
In dem vorliegenden Fall ist es relativ simpel, da du ja das Array nur innerhalb deiner Methode benötigst. Es genügt daher, wenn du deine Strings in einer lokalen Variable hast (was ja schon der Fall ist) und du nicht versuchst den String in den Record zu bekommen, sondern (wie in den Links gezeigt) nur die Pointer auf diese vorhandenen Strings.
So habe ich das auch schon für die Verwendung mit Format gemacht.
jasocul hat folgendes geschrieben : | Format habe ich bisher noch nie genutzt. |
Wir benutzen das um Ausgabestrings nicht per Hand zusammenzuklöppeln, was einfach die Übersichtlichkeit verringern würde und die Übersetzung in andere Sprachen deutlich erschweren oder unmöglich machen würde.
|
|
jasocul
Beiträge: 6388
Erhaltene Danke: 146
Windows 7 + Windows 10
Sydney Prof + CE
|
Verfasst: Fr 22.03.19 11:03
Off-Topic:
jaenicke hat folgendes geschrieben : | jasocul hat folgendes geschrieben : | Format habe ich bisher noch nie genutzt. | Wir benutzen das um Ausgabestrings nicht per Hand zusammenzuklöppeln, was einfach die Übersichtlichkeit verringern würde und die Übersetzung in andere Sprachen deutlich erschweren oder unmöglich machen würde. |
Genau dieser Bedarf existiert bei uns nicht/kaum, da wir nur Inhouse-Entwicklung machen. Es gibt zwar Anwendungen, die Protokolle erzeugen, aber die werden nur von der IT genutzt. Großartige Formatierungen sind daher nicht erforderlich.
On-Topic:
Die Strings sind eben nicht in einer lokalen Variablen. Es wird als Konstante (in einem Array of const) an die Verarbeitungsroutine übergeben. Somit wird der reservierte Speicher wieder frei gegeben und im späteren Schleifendurchlauf wieder genutzt. Das führt ja genau zu dem beschriebenen Problem.
|
|
jaenicke
Beiträge: 19285
Erhaltene Danke: 1743
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Fr 22.03.19 11:42
|
|
jasocul
Beiträge: 6388
Erhaltene Danke: 146
Windows 7 + Windows 10
Sydney Prof + CE
|
Verfasst: Fr 22.03.19 12:38
Ah, ok.
Das hatte ich bei der neuen Source-Variante übersehen.
|
|
|