Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Risiko dyn.array als Funktionsrückgabewert ??? (https://www.delphipraxis.net/166988-risiko-dyn-array-als-funktionsrueckgabewert.html)

bernhard_LA 8. Mär 2012 14:25

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:
aList : :TINTEGERList2D ;
           bList : :TINTEGERList2D ;


aList[i,j] := Blist [u,v];
ein mögliche Ursache ?

himitsu 8. Mär 2012 14:47

AW: Risiko dyn.array als Funktionsrückgabewert ???
 
Delphi-Quellcode:
aList[i,j] := Blist [u,v];
Ist ja im Endeffekt nur eine einfache Integerzuweisung und macht keine Probleme.
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:
aList := bList;
oder
Delphi-Quellcode:
aList[i] := bList[u];
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.

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

bernhard_LA 8. Mär 2012 15:29

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:

uligerhardt 8. Mär 2012 15:31

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:
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.
bei mir
Code:
Return: 0 10
var:    0 10
out:    0  0
DynArray-Rückgabewerte werden also nicht korrekt initialisiert!

Peter1999 8. Mär 2012 16:11

AW: Risiko dyn.array als Funktionsrückgabewert ???
 
Zitat:

DynArray-Rückgabewerte werden also nicht korrekt initialisiert!
Wieso? Was hättest du denn da anderes erwartet?

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?

himitsu 8. Mär 2012 16:42

AW: Risiko dyn.array als Funktionsrückgabewert ???
 
Zitat:

Zitat von Peter1999 (Beitrag 1155430)
Zitat:

DynArray-Rückgabewerte werden also nicht korrekt initialisiert!
Wieso? Was hättest du denn da anderes erwartet?

Mit der Speicherverwaltung gibt es eigentlich keine Probleme.

Aber mit der Initialisierung muß man aufpassen.
http://www.delphipraxis.net/166950-s...ml#post1155075 (#16-#18)

uligerhardt 8. Mär 2012 16:45

AW: Risiko dyn.array als Funktionsrückgabewert ???
 
Zitat:

Zitat von Peter1999 (Beitrag 1155430)
Zitat:

DynArray-Rückgabewerte werden also nicht korrekt initialisiert!
Wieso? Was hättest du denn da anderes erwartet?

Na halt, dass sie korrekt initialisiert werden. :mrgreen:
Eine lokale verwaltete Variable (String, dyn. Array, Interface) wird ja auch null-initialisiert.

Zitat:

Zitat von Peter1999 (Beitrag 1155430)
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?

Damit meinst du jetzt den OP, oder?

Panthrax 8. Mär 2012 16:49

AW: Risiko dyn.array als Funktionsrückgabewert ???
 
Zitat:

Zitat von uligerhardt (Beitrag 1155445)
Zitat:

Zitat von Peter1999 (Beitrag 1155430)
Zitat:

DynArray-Rückgabewerte werden also nicht korrekt initialisiert!
Wieso? Was hättest du denn da anderes erwartet?

Na halt, dass sie korrekt initialisiert werden. :mrgreen:
Eine lokale verwaltete Variable (String, dyn. Array, Interface) wird ja auch null-initialisiert.


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;

himitsu 8. Mär 2012 17:00

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.

shmia 8. Mär 2012 17:19

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.

himitsu 8. Mär 2012 17:28

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.

Peter1999 8. Mär 2012 21:24

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

uligerhardt 9. Mär 2012 07:32

AW: Risiko dyn.array als Funktionsrückgabewert ???
 
Zitat:

Zitat von Peter1999 (Beitrag 1155498)
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...

Mit dem Beispiel wollte ich zeigen, dass die Rückgabe von compilerverwalteten Typen sich wie eine var-Übergabe vehält, wo doch semantisch gesehen eine out-Übergabe angemessen wäre:
Zitat:

Zitat von http://docs.embarcadero.com/products/rad_studio/radstudio2007/RS2007_helpupdates/HUpdate4/EN/html/devcommon/parameters_xml.html
With an out parameter, however, the initial value of the referenced variable is discarded by the routine it is passed to. The out parameter is for output only; that is, it tells the function or procedure where to store output, but doesn't provide any input.

"output only" und "where to store output" hört sich für mich irgendwie schon nach Rückgabewert an.

bernhard_LA 9. Mär 2012 17:42

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

himitsu 9. Mär 2012 18:07

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 19:23 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