Autor Beitrag
Xion
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
EE-Maler
Beiträge: 1952
Erhaltene Danke: 128

Windows XP
Delphi (2005, SmartInspect), SQL, Lua, Java (Eclipse), C++ (Visual Studio 2010, Qt Creator), Python (Blender), Prolog (SWIProlog), Haskell (ghci)
BeitragVerfasst: Di 12.02.13 20:37 
Hi,

wenn ich ein Record anlege, wird es standardmäßig freigeben. Z.B:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
type TInt=record
  i: integer;
end;
  
procedure DoSomething;
var R: TIntRec;
begin
  R.i:=50;
end;


Wenn ich das Record als Rückgabewert verwende, dann wird es (vorerst) nicht freigegeben.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
function DoSomething: TIntRec;
begin
  Result.i:=50;
end;


Jetzt zur Frage:
Was passiert mit dem Record, wenn ich einen Pointer darauf z.B. in eine Liste einfüge:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
var List: TList;

procedure DoSomething;
var R: TIntRec;
begin
  R.i:=50;
  List.Add(@R);
end;


Wann wird es nun freigegeben? Hoffentlich nicht beim Verlassen der Prozedur, sonst ist der Pointer in der Liste ja ungültig. Lösche ich nun den Pointer mit List.Delete(0), dann sollte das Record möglichst freigegeben werden, da ja nun kein Pointer mehr darauf existiert. Ist dies der Fall? Wenn ja, führt Delphi ein Reference Counting um zu bemerken, wenn kein Pointer mehr auf das Record zeigt? Das wäre in diesem Fall dann ziemlich Speicheraufwändig, da ich den Counter für jeden Integer benötige (also 50% overhead).

Unterm Strich die Frage:
Wenn ich das Record wie oben in die Liste einfüge, muss ich mir dann irgendwelche Sorgen um den Speicher machen? Ich habe lediglich Themen dazu gefunden, bei denen ein Pointer darauf mit New() angelegt wurde, der dann natürlich mit Dispose wieder freigegeben werden musste.

Habe dazu leider keine Antwort im Netz gefunden. Meine Tests vermitteln den Eindruck, dass Delphi es genau so macht wie ich es haben will (wie eigentlich immer, genau invers zu C++ :P).

Danke für die Antworten


Moderiert von user profile iconNarses: Topic aus Sonstiges (Delphi) verschoben am Mi 13.02.2013 um 00:03

_________________
a broken heart is like a broken window - it'll never heal
In einem gut regierten Land ist Armut eine Schande, in einem schlecht regierten Reichtum. (Konfuzius)
WasWeißDennIch
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 653
Erhaltene Danke: 160



BeitragVerfasst: Di 12.02.13 20:46 
Nach meiner Kenntnis wird der Record wie alle lokalen Nicht-Pointer-Variablen nach Verlassen der Routine ungültig. Deshalb sollte man das wohl eher so schreiben:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
var List: TList;

procedure DoSomething;
var R: PIntRec;
begin
  New(R);
  R^.i:=50;
  List.Add(R);
end;


[edit] Oder anders ausgedrückt: AFAIK landen lokale Variablen auf dem Stack und werden nach Verlassen der enthaltenden Routine ungültig. Das betrifft in meinem Beispiel auch R, d.h. der Pointer R ist flüchtig, nicht aber die dahinterstehenden Daten. Durch das Hinzufügen zur Liste hingegen wird er dann auf dem Heap abgelegt und ist somit verfügbar. Genaugenommen ist das dann aber nicht mehr der "Original-R", sondern eine Kopie davon. [/edit]

Für diesen Beitrag haben gedankt: Martok, Xion
Xion Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
EE-Maler
Beiträge: 1952
Erhaltene Danke: 128

Windows XP
Delphi (2005, SmartInspect), SQL, Lua, Java (Eclipse), C++ (Visual Studio 2010, Qt Creator), Python (Blender), Prolog (SWIProlog), Haskell (ghci)
BeitragVerfasst: Di 12.02.13 21:30 
user profile iconWasWeißDennIch hat folgendes geschrieben Zum zitierten Posting springen:
Oder anders ausgedrückt: AFAIK landen lokale Variablen auf dem Stack und werden nach Verlassen der enthaltenden Routine ungültig

Ah, ok, alles klar. Und wenn ich das Record als Rückgabewert zurückgebe, dann bleibt es vorerst auf dem Stack, bis es dann später vom Stack fliegt, wenn die aufrufende Prozedur endet?

_________________
a broken heart is like a broken window - it'll never heal
In einem gut regierten Land ist Armut eine Schande, in einem schlecht regierten Reichtum. (Konfuzius)
GuaAck
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 378
Erhaltene Danke: 32

Windows 8.1
Delphi 10.4 Comm. Edition
BeitragVerfasst: Di 12.02.13 22:25 
... oder Du eine andere FUNCTION oder PROCEDURE aufrufst, die den Stack überschreibt, also alles mehr oder weniger Zufall. Also entweder per NEW oder in einer übergordneten Variable. Übrigens: Wenn im Compiler die entsprechenden Warnungen aktiviert sind, dann sollte die Warnung "unsicher Code @Operator" kommen, genau aus den angeführten Gründen.

Gruß
GuaAck
Xion Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
EE-Maler
Beiträge: 1952
Erhaltene Danke: 128

Windows XP
Delphi (2005, SmartInspect), SQL, Lua, Java (Eclipse), C++ (Visual Studio 2010, Qt Creator), Python (Blender), Prolog (SWIProlog), Haskell (ghci)
BeitragVerfasst: Mi 13.02.13 20:30 
user profile iconGuaAck hat folgendes geschrieben Zum zitierten Posting springen:
Also entweder per NEW oder in einer übergordneten Variable.

Das gilt, wenn ich einen Pointer zurück gebe, oder? Wenn ich das Record selbst zurück gebe, dann wird eine Kopie zurückgegeben. Genau wie bei Integer. Oder etwa nicht?

Also mein neues Wissen mal angewendet:
ausblenden volle Höhe Delphi-Quelltext
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:
type TRec=record
  i: integer;
end;      

function Generate: TRec;
begin
  Result.i:=25;
end;

procedure Insert;
var R: TRec;
    iP: ^Integer;
begin
  R:=Generate; //das sollte ja gut gehen

  new(iP);
  iP^:=R.i;  //der Pointer ist natürlich nicht auf das Record (Stack), sondern auf einen Int-Speicherplatz auf dem Heap, und hat mit dem Record nix zu tun
  List.Add(iP);
end;

procedure Delete;
var iP: ^Integer;
begin
  iP:=List.First;
  List.Delete(0);
  Dispose(iP);  //int auf heap wieder freigeben
end;

procedure Main;
begin
  List:=TList.Create;

  Insert;
  Delete;

  List.Free;
end;


Ok so, oder?

PS:
Hab jetzt die enstprechende Compiler-Warnung gefunden und aktiviert. Bei obigem Code sagt er:
[Warnung] (Zeile 17) W1047 Unsicherer Code '^-Operator'
Die Warnung kommt auch, wenn ich direkt Schreibe: iP^:=25;. Vorschläge? ;)
FastMM4 findet zumindest keine Speicherleaks, also das ist ok.

Edit2:
Ich habe mehrfach gelesen, dass die Warnung lediglich was damit zu tun hat, das man mit .Net ^ und @ usw. nicht mehr benutzen kann. "sicher" bezieht sich daher eher auf "deprecated". Ich frage mich, wie ich eine generische Datenstruktur dann überhaupt machen soll, ohne Pointer ;) Delphi 2005 kennt ja noch keine Generics. Für alles Klassen? Bäh!

_________________
a broken heart is like a broken window - it'll never heal
In einem gut regierten Land ist Armut eine Schande, in einem schlecht regierten Reichtum. (Konfuzius)