![]() |
Risiko dyn.array als Funktionsrückgabewert ???
wir haben einen Datentyp
Delphi-Quellcode:
TINTEGERList2D = array of array of Integer
Im Program gibt es jede Menge an Funktionen wie
Delphi-Quellcode:
...; function Make_Something (var aList :TINTEGERList2D ) : Real; function Make_SomethingOther (x,y : Integer) : TINTEGERList2D; ...; Wir hatten das Programm schon im Einsatz, lief ganz gut. Nach den letzten Änderungen bekommen wir eine AV Delphi versucht eines unserer Felder/Listen 2 x freizugeben (lt. FASTMM4) . Nur sehen wir leider nicht die Position im Code .... und warum sehe ich erst recht nicht Sind Zuweisungen wir
Delphi-Quellcode:
ein mögliche Ursache ?
aList : :TINTEGERList2D ;
bList : :TINTEGERList2D ; aList[i,j] := Blist [u,v]; |
AW: Risiko dyn.array als Funktionsrückgabewert ???
Delphi-Quellcode:
Ist ja im Endeffekt nur eine einfache Integerzuweisung und macht keine Probleme.
aList[i,j] := Blist [u,v];
Ihr könnt aber dennoch mal, in den Projektoptionen, die Bereichsprüfung aktivieren, denn Schreibzugriffe auf nichtexistierende Fehler bereiten manchmal kleine Problemchen. :angle: Aber
Delphi-Quellcode:
oder
aList := bList;
Delphi-Quellcode:
sind da etwas anders zu betrachten, denn dyn. Arrays verfügen zwar über eine Referenzzählung, aber man hat "vergessen" diesen Arrays einen CopyOnWrite-Zugriff zu verpassen.
aList[i] := bList[u];
Wenn man bei Strings
Delphi-Quellcode:
var
s, x: string; s := '12345'; x := s; // erhöht nur die Referenzzählung x[3] := 'a'; // führt vorher ein UniqueString auf x aus, bevor geschrieben wird. ShowMessage(s + sLineBreak + x);
Delphi-Quellcode:
var
s, x: array of Integer; SetLength(s, 5); s[0] := 1; s[1] := 2; s[2] := 3; s[3] := 4; s[4] := 5; x := s; // erhöht auch nur die Referenzzählung x[2] := 666; // schreibt einfach, ohne die Referenzzählung zu prüfen ShowMessage(IntToStr(s[2]) + ' ' + IntToStr(x[2]));
Delphi-Quellcode:
...
s[4] := 5; x := Copy(s); // kopiert das array, in eine eigenständige Instanz x[2] := 666; // schreibt einfach, ohne die Referenzzählung zu prüfen ShowMessage(IntToStr(s[2]) + ' ' + IntToStr(x[2]));
Delphi-Quellcode:
...
s[4] := 5; x := s; // erhöht auch nur die Referenzzählung x := Copy(x); // sicherstellen, daß x unique ist (zur Optimierung könnte man vorher noch die referenzzählung prüfen, ob überhaupt nötig) x[2] := 666; // schreiben (die Referenzzälung wurde ja schon sichergestellt) ShowMessage(IntToStr(s[2]) + ' ' + IntToStr(x[2])); Aber so oder so, an der Referenzzählung sollte nichts kaputtgehn, abgesehn - von Bufferoverruns - wenn man die Array-Instanz-Referenzzählung direkt oder indirekt mit Pointern schrottet/umgeht |
AW: Risiko dyn.array als Funktionsrückgabewert ???
Bereichsprüfung eingeschalten -> leider kein Erfolg
innerhalb der Funktionen sind die Setlenght Commands verbaut, alle dyn. Arrays werden als var Param übergeben, bzw. als Function result abgeholt .... kann hier irgendwo ein Fehler liegen im Speichermanager ??? Wie finde ich den Fehler :cry: |
AW: Risiko dyn.array als Funktionsrückgabewert ???
Ich weiß nicht, ob das dein Problem ist, aber bei dynamischen Arrays als Rückgabewerten muss man beachten, dass sie eher als var-Parameter implementiert sind. Z.B. liefert folgendes Progrämmchen
Delphi-Quellcode:
bei mir
program DynArrayReturn;
{$APPTYPE CONSOLE} uses Types, SysUtils; function MakeArrayRet(ASize: Integer): TIntegerDynArray; begin Write(Length(Result): 3); SetLength(Result, ASize); end; procedure MakeArrayVar(var AArray: TIntegerDynArray; ASize: Integer); begin Write(Length(AArray): 3); SetLength(AArray, ASize); end; procedure MakeArrayOut(out AArray: TIntegerDynArray; ASize: Integer); begin Write(Length(AArray): 3); SetLength(AArray, ASize); end; procedure Test; var a: TIntegerDynArray; begin Write('Return:'); a := nil; a := MakeArrayRet(10); a := MakeArrayRet(5); Writeln; Write('var: '); a := nil; MakeArrayVar(a, 10); MakeArrayVar(a, 5); Writeln; Write('out: '); a := nil; MakeArrayOut(a, 10); MakeArrayOut(a, 5); Writeln; end; begin try Test; Readln; except on E: Exception do Writeln(E.Classname, ': ', E.Message); end; end.
Code:
DynArray-Rückgabewerte werden also nicht korrekt initialisiert!
Return: 0 10
var: 0 10 out: 0 0 |
AW: Risiko dyn.array als Funktionsrückgabewert ???
Zitat:
Für viel wahrscheinlicher halte ich, dass an irgendeiner Stelle der Array nicht sauber von 0..high() durchlaufen wird. Ist da versehentlich ein 1..length() dazwischen geraten? Wenn das passiert, treten die Zugriffsverletzungen leider nur selten sehr zeitnah auf und man sucht seeeehr lange. Dass es an einer doppelten Freigabe des Speichermanagers liegt, halte ich eher für unwahrscheinlich. Wie kommst du denn zu der Erkenntnis? |
AW: Risiko dyn.array als Funktionsrückgabewert ???
Zitat:
Aber mit der Initialisierung muß man aufpassen. ![]() |
AW: Risiko dyn.array als Funktionsrückgabewert ???
Zitat:
Eine lokale verwaltete Variable (String, dyn. Array, Interface) wird ja auch null-initialisiert. Zitat:
|
AW: Risiko dyn.array als Funktionsrückgabewert ???
Zitat:
Sie sind korrekt initialisiert. Funktionsrückgabewerte sind eben keine lokalen Variablen.
Delphi-Quellcode:
function F(const X: Extended): Extended; begin Result := { ... }; end;
procedure F(const X: Extended; var Result: Extended); begin Result := { ... }; end; |
AW: Risiko dyn.array als Funktionsrückgabewert ???
Ordinale Typen, Fließkommatypen und alles mögliches kleines einfaches Getier, welches in ein Register paßt und wo kein schliommes Speichermanagement dahintersteht,
das sind "richtige" Rückgabewerte und keine impliziten Out/Var-Parameter. Aber bei diesen Sachen, wie String und dyn. Array, ist das soeine Sache. |
AW: Risiko dyn.array als Funktionsrückgabewert ???
Also ich würde dyn. Arrays nur innerhalb einer Klasse umherreichen.
Sobald das dyn. Array die Grenzen einer Klasse verlässt diese Technik nicht robust genug, um Daten sicher zu transportieren. Ein dyn. Array ist wie DNS ohne eine schützende Zellwand drumrum; jeder kann (auch unabsichtliche) Änderungen vornehmen. Wenn man das dyn. Array mit einer Klasse kapselt, kann man den Zugriff auf die inneren Daten genau kontrollieren. |
AW: Risiko dyn.array als Funktionsrückgabewert ???
Ich nutze diese gerne mal, als Rückgabewerte, für irgendwelche kleineren Listen, denn hier muß sich keiner um die Speicherfreigabe kümmern.
Nicht umsonst sollte z.B. keine TStringList als Result nutzen, da dort die Sache mit der Freigabe nicht so schön zu klären ist. |
AW: Risiko dyn.array als Funktionsrückgabewert ???
@Himitsu
Also ich nutze sie nun eben gerade gern für große Datenmengen, da hier der riesen Overhead der Objekte (TList<Real>, o.Ä.) entfällt und RAM ist zumindest bei mir immer noch kostbar. Aber das ist wohl Geschmacksache. @uligerhardt Ich finde immer noch, das Beispiel ist ungeeignet, da alles genau so ist, wie man es auch erwartet. Wenn nil, dann length=0...wenn out, dann geht nix rein, also auch nil--> length=0, wenn das setlength vor dem write steht, stimmt auch die Länge=10 usw... @Himitsu Teil2 ;) Du hast natürlich recht, dass man beim Result von Funktionsaufrufen aufpassen muss, ob man selbst sauber initialisiert. Doch da sollte man dann auch eine Compiler-Warnung bekommen, oder nicht? Außerdem ist das ja nicht nur bei DynArrays der Fall sondern generell. @bernhard_LA Um mal wieder zum eigentlichen Thema zu kommen. Gibt es denn noch irgendwelche Hinweise (Fehlermeldung, Code-Schnippsel,...)? Sonst ist das hier wahrscheinlich wenig Erfolg versprechend. Gute Nacht |
AW: Risiko dyn.array als Funktionsrückgabewert ???
Zitat:
Zitat:
|
AW: Risiko dyn.array als Funktionsrückgabewert ???
fehler gefunden : falscher Zugriff aber ne ganze menge code zeilen vor AV --> lange suche :-( .... die jetzt ein Ende hat :-D
|
AW: Risiko dyn.array als Funktionsrückgabewert ???
Gerade bei großen Datenmengen kann der Overhead des TList-Objekte schnell man nicht mehr ins Gewicht fallen.
Denn der reine Verwaltungsoverhead ist garnicht so groß. (nur ein paar wenig Byte) Das Einzige, was größer auffallen könnte, ist eine Geschwindigkeits- und Speicheroptimierung. So wird beim .Add nicht nur für das eine Feld Speicher reserviert, sondern gleich für ein paar mehr. Vorteil ist dann, daß bei den nächsten Add keine Speicheranforderungen mehr nötig sind, was das umkopieren des Speichers oftmals erspart. Außerdem haben Liste auch andere Vorteile, was den größeren Overhead auch relativieren kann. (nette Funktionen, welche man nutzen kann) Es kommt aber immer auf den Einzelfall an, denn eine TList ist intern auch "nur" ein Array und je nach Anforderung hat alles mal seine Vor- und Nachteile. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 23:31 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