Parameter soll Kopie statt Zeiger übergeben

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Parameter soll Kopie statt Zeiger übergeben

Beitrag von mschnell »

Michl hat geschrieben:Mit dem >var< bei der Parameterübergabe signalisiert man der Procedure/Function dass mit dem übergebenen Parameter gearbeitet werden soll, nicht mit einer Kopie von ihm.
Das ist zwar technisch korrekt, nützt dem User aber nicht allzu viel, wenn er sich nicht darüber klar ist, dass Objekte und dynamische Arrays (nicht aber Strings !) technisch gesehen von sich aus schon Zeiger auf die enthaltenen Daten sind und nicht die Daten selbst repräsentieren. Da werden die Daten nicht kopiert. (Bei Strings auch nicht, die tun aber so als ob sie kopiert würden: "lazy copy").

Ein Objekt oder ein Array als nicht-var heißt nicht: "die enthaltenen Daten werden kopiert und Änderungen an den Daten sind beim Caller nicht sichtbar", sondern "der Zeiger wird kopiert und man kann das Objekt nicht (ohne kopieren der enthaltenen Daten) durch eine andere Instanz dieses Typs ersetzen". Bei "var" kann man also der variable eine andere Instanz des Objektes zuweisen.

-Michael
Zuletzt geändert von mschnell am Mi 13. Mär 2013, 09:54, insgesamt 1-mal geändert.

Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

Re: Parameter soll Kopie statt Zeiger übergeben

Beitrag von Michl »

Code: Alles auswählen

und ob:
 
Code: Alles auswählen
 
    type
      tdyn = array of integer;
    function xx: tdyn;
    begin
     SetLength(Result, 10);
     result[1] := 99;
    end;
    procedure TForm1.FormCreate(Sender: TObject);
    var
      x: tdyn;
    begin
     x := xx();
     caption := IntToStr(x[1]);
    end;
Hab es gerade probiert - das geht!

EDIT ... es wird jedoch nur der Refenzwert kopiert!!!
Zuletzt geändert von Michl am Mi 13. Mär 2013, 10:31, insgesamt 2-mal geändert.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection;  

Soner
Beiträge: 733
Registriert: Do 27. Sep 2012, 00:07
OS, Lazarus, FPC: Win10Pro-64Bit, Immer letzte Lazarus Release mit SVN-Fixes
CPU-Target: x86_64-win64
Wohnort: Hamburg

Re: Parameter soll Kopie statt Zeiger übergeben

Beitrag von Soner »

Also ausgehend vom dritten Beitrag hätte es gereicht, zur Sicherheit vor dem Funktionsparameter const zusetzen:
function tAlgorithmus.zurueck(const pTeilloesung : TArray) : TArray;

Dann die Zahlen in eine Schleife einzeln kopieren:

Code: Alles auswählen

 
function tAlgorithmus.zurueck(const pTeilloesung : TArray) : TArray;
// ......
begin
  setLength(lTeilloesung, zSize-1);
  //NICHT DAS: lTeilloesung := pTeilloesung;
  //SONDERN EINZELN Kopieren:
  for i:=0 to zSize-1 do 
    lTeilloesung[i] := pTeilloesung[i];
 
 // ....
  if Laenge(lTeilloesung) > 0 then
    result := lTeilloesung
  else begin
    //NICHT DAS result := pTeilloesung;
    //SONDERN EINZELN Kopieren:
     for i:=0 to zSize-1 do 
        lTeilloesung[i] := pTeilloesung[i];
    result := lTeilloesung
  end;
end;
 

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Parameter soll Kopie statt Zeiger übergeben

Beitrag von mschnell »

Michl hat geschrieben:Hab es gerade probiert - geht wirklich,
Das ist ja auch sinnvoll. Das, was bei Delphi (zumindest ältere Version) geht (also in Result steht bei Start des Codes der Funktion schon was sinnvolles drin), bei fpc aber nicht, ist eher verwirrend.

-Michael
Zuletzt geändert von mschnell am Mi 13. Mär 2013, 09:55, insgesamt 1-mal geändert.

Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

Re: Parameter soll Kopie statt Zeiger übergeben

Beitrag von Michl »

Hat mich ganz schön beschäftigt, wieso ich bisher immer davon ausgegangen bin, dass man keine dynamischen Arrays per Funktion zuweisen kann, bzw. wenn man dies tut immer nur die Referenz genutzt wird. Habe folgendes gefunden:

http://www.delphi-treff.de/tutorials/ob ... rs/arrays/
Arrays kopieren

Ein Array zu kopieren ist nicht so trivial, wie dies bei einer Variable ist. Bei einer Variable könnte man diese einfach einer anderen zuweisen. Bei einem Array funktioniert eine solche Zuweisung nicht! Eine Zuweisung wie diese

array1 := array2;

hätte ganz einfach den Effekt, dass nun "array1" ein Synonym für "array2" ist, dass heißt, dass "array1" und "array2" das selbe Array nur mit einem anderen Namen sind. "array1" ist dann eine Referenz auf "array2". Erinnern Sie sich an "call by reference"!

Aber natürlich gibt es eine Möglichkeit, auch ein Array zu kopieren und zwar mit der Funktion "copy". Diese erhält wahlweise einen oder drei Parameter. In der ersten Variante ist der einzige Parameter das zu kopierende Array, bei der zweiten Variante wird angegeben ab welchem Index wie viele Elemente kopiert werden sollen. Zwei Beispiele machen das deutlicher:

array1 := Copy(array2); //kopiert array2 komplett in array1
array1 := Copy(array2, 3, 4); //kopiert 4 Elemente beginnend mit Index 3

Aber Vorsicht: wenn ein Array einen Refernzdatentyp enthält, müssen die Elemente einzeln kopiert werden, dann erstellt auch Copy nur ein Array, welches Referenzen auf die selben Objekte enthält.
Ich habe folgendes probiert:
array1 := array2;
sowie
array1 := copy(array2);
sowie
array1 unverändert

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
var
  Array1, Array2: Array of integer;
  i:integer;
  p1,p2:pointer;
begin
  SetLength(Array1,100);
  for i:=0 to 99 do Array1[i]:=i;
  SetLength(Array2,1000);
  for i:=0 to 999 do Array2[i]:=i;
 
  Array1:=Array2;
//  Array1:=copy(Array2);
 
  p1:=@Array1;
  p2:=@Array2;
  showmessage('P1['+inttostr(integer(p1))+'] P2['+inttostr(integer(p2))+']');
end; 
 
Jedesmal das gleiche Ergebnis, ob ich Array1:=Array2 oder ob ich Array1:=copy(Array2) oder ob ich gar keine Operation durchführe, P1 zeigt immer auf einen anderen Speicher als P2. Oder anders ausgedrückt die Referenz wird nie kopiert sondern immer nur die Werte des Arrays. Sehe ich das richtig oder habe ich schon wieder einen Denkfehler?! Oder ist dieses Verhalten nur bei Lazarus so und bei Delphi anders?

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection;  

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: Parameter soll Kopie statt Zeiger übergeben

Beitrag von mse »

Michl hat geschrieben:

Code: Alles auswählen

 
  p1:=@Array1;
  p2:=@Array2;
  showmessage('P1['+inttostr(integer(p1))+'] P2['+inttostr(integer(p2))+']');
 

Code: Alles auswählen

 
  p1:= pointer(Array1);
  p2:= pointer(Array2);
 
"@Arrayx" bringt die Adresse der Varable worin der pointer zum array-Block im Speicher abgelegt ist.

Martin

Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

Re: Parameter soll Kopie statt Zeiger übergeben

Beitrag von Michl »

p1:= pointer(Array1);
p2:= pointer(Array2);
Ja, danke, so war das! Ist zu lange her bzw. ist zu lange gestern gewesen :) , und die rückgelieferten Pointer (bzw. deren Abstand voneinander) entsprechen eher der Größe von den Arrays, hatte mich schon gewundert.

So nun trotzdem die Frage funktioniert eine Function vom typ "array of...".

Ich habe folgendes probiert:

Code: Alles auswählen

 
type
  typeofiArray = array of integer; 
...
function irgend:typeofiarray;
var
  i:integer;
begin
  setlength(irgend,100);
  for i:=0 to 99 do irgend[i]:=i;
end;
 
procedure TForm1.Button1Click(Sender: TObject);
var
  Array1, Array2: Array of integer;
  i:integer;
  p1,p2:pointer;
begin
  SetLength(Array1,100);
  for i:=0 to 99 do Array1[i]:=i;
  SetLength(Array2,1000);
  for i:=0 to 999 do Array2[i]:=i;
 
  Array1:=Array2;       //jetzt sind die Referenzen identisch!!! (ich habe somit eine toten Speicher generiert)
//  Array1:=copy(Array2);
  Array1:=irgend;       //jetzt liegt die Referenz von Array1 von dem von Function "irgend" erzeugten Array -> auch wieder ein Speicherleck!
 
  p1:=pointer(Array1);
  p2:=pointer(Array2);
  showmessage('P1['+inttostr(integer(p1))+'] P2['+inttostr(integer(p2))+']');
end; 
Wenn ich Array1 eine Function vom typ "array of..." zuweise, bekomme ich doch somit nur die Referenz zurück und die alte ist futsch?! Ist das richtig oder liegt da ein Denkfehler vor?

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection;  

Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

Re: Parameter soll Kopie statt Zeiger übergeben

Beitrag von Michl »

Ja, wenn ich den Code von Michael ansehe, macht er ja eigentlich auch nichts anderes. Mit der Function "xx: tdyn;" generiert er ja ein neues "Array of..".

Code: Alles auswählen

type
  tdyn = array of integer;
function xx: tdyn;
begin
 SetLength(Result, 10);
 result[1] := 99;
end;
Also ist es doch so, dass immer nur der Zeiger kopiert wird und nicht das >array an sich< oder besser verständlich ausgedrückt nicht die Werte werden kopiert, was ja die Frage war!

daher macht doch eine Function(Array1:Array of..):Array of.. doch keinen Sinn, da dann immer nur die Referenz der Funktion kopiert wird. Somit kann man doch auch eine Procedure nehmen, der als Parameter das "Array of" mitschicken, wo dann direkt dieses genutzt wird?!

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection;  

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Parameter soll Kopie statt Zeiger übergeben

Beitrag von mschnell »

Michl hat geschrieben:Arrays kopieren

Ein Array zu kopieren ist nicht so trivial, wie dies bei einer Variable ist. Bei einer Variable könnte man diese einfach einer anderen zuweisen. Bei einem Array funktioniert eine solche Zuweisung nicht! Eine Zuweisung wie diese ...
Bitte klar zwischen dynamischen Arrays und statischen (bei denen die Anzahl der Element bei der Definition angegeben ist) unterscheiden ! Die verhalten sich völlig unterschiedlich.

Die Notation ist bei Strings auch identisch:

Mystring := MyString2;
Mystring[3] := MayString[4];

die verhalten sich aber noch anders und auch wieder unterschiedlich je nachdem was für eine Art String das ist.

MystringA: String[7]; // Short String verhält sich wie ein nicht-dynamische Array

MyStringB: String; // Long String: ist dynamisch, verhält sich durch "Reference Counting" und "lazy Copy" aber wie ein nicht dynaisches Array

MyStringC: WideString; // Ich glaube zumindest auf Windows ist das nicht "Reference Counting", verhält sich also wie ein dynamisches Array. Da bin ich aber nicht sicher.


Finde ich ziemlich verwirrend, ist aber nicht zu ändern.

-Michael

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Parameter soll Kopie statt Zeiger übergeben

Beitrag von mschnell »

Michl hat geschrieben:daher macht doch eine Function(Array1:Array of..):Array of.. doch keinen Sinn ...
Ich habe mal eine Matrix-Library mit dynamische Arrays geschrieben. Da sind Vektoren dynamische Array und Matrizen dynamische Arrays von Vektoren (also im Endeffekt "Arry of Array of Extended").-

Beispielsweise die Funktion zum Addieren von Matrizen hatte also ein "Arry of Array of.." als Result.

Funktioniert prima.

Man kann immer eine "Procedure" anstelle einer "Function" nehmen und als Rückgabewert einen var-Parameter definieren. Die Verwendung ist aber deutlich weniger praktisch zu formulieren.

Siehe Matrix-Library:

MatrixA := MatrixMult(MatrixAdd(MatrixB, MatrixC), MatrixD);

Und dann kann man noch Operator Overload definieren:

MatrixA := (MatrixB + MatrixC) * MatrixD;

-Michael
Zuletzt geändert von mschnell am Mi 13. Mär 2013, 10:22, insgesamt 1-mal geändert.

Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

Re: Parameter soll Kopie statt Zeiger übergeben

Beitrag von Michl »

Bitte klar zwischen dynamischen Arrays und statischen (bei denen die Anzahl der Element bei der Definition angegeben ist) unterscheiden ! Die verhalten sich völlig unterschiedlich.
... dies ist nur ein Auszug der zuvor gelinkten Webseite und bezieht sich in diesem Fall nur auf dynamische Arrays. Da dieses Forum sich bisher fast nur auf diese bezogen, habe ich das nicht extra geschrieben. :wink:

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection;  

Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

Re: Parameter soll Kopie statt Zeiger übergeben

Beitrag von Michl »

Ich habe mal eine Matrix-Library mit dynamische Arrays geschrieben. Da sind Vektoren dynamische Array und Matrizen dynamische Arrays von Vektoren (also im Endeffekt "Arry of Array of Extended").-

Beispielsweise die Funktion zum Addieren von Matrizen hatte also ein "Arry of Array of.." als Result.

Funktioniert prima.

-Michael
Das ist doch auch prima! Für Deine Anwendung und es gibt sicherlich auch noch viele andere, wo dieses Vorgehen Sinn macht! Ich habe mich eigentlich hauptsächlich auf den Fragenden bezogen. In diesem Fall macht es doch aber keinen Sinn, erst ein Array in eine Funktion zu senden und ein anderes als Result zu bekommen, da kann man dieses doch gleich nehmen!

Auf jeden Fall habe ich dadurch mich wieder einmal mit den Grundlagen befasst und in Erinnerung gerufen und bin beruhigt, dass wir nur aneinander vorbei geredet haben! Ich schick Dir dafür ein paar Blumen :) :) :)

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection;  

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Parameter soll Kopie statt Zeiger übergeben

Beitrag von mschnell »

Beispiel:

Code: Alles auswählen

Var
  s1, s2: String;
  a1, a2: array of char;
 
procedure TForm1.FormCreate(Sender: TObject);
var
  i: Integer;
begin
//  SetLength(s1,3);
  s1 := 'yxz';
  SetLength(a1,3);
  a1[0] := 'x';
  a1[1] := 'y';
  a1[2] := 'z';
 
  s1[2] := '1';
  a1[1] := '1';
 
  s2 := s1;
  s1[3] := '2';  // wirkt sich nicht auf s2 aus ("lazy copy")
  a2 := a1;
  a1[2] := '2';  // wirkt sich auf a2 aus (pointer copy)
 
  caption :=   s2;
  for i := low(a2) to high(a2) do begin
    caption := caption +  a2[i];
  end;
end;

Antworten