Parameter soll Kopie statt Zeiger übergeben

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
maeries
Beiträge: 6
Registriert: Fr 8. Mär 2013, 17:39

Parameter soll Kopie statt Zeiger übergeben

Beitrag von maeries »

Hallo zusammen,
ich hoffe, dass dies hier das richtige Forum ist.
Ich habe folgendes Problem: Ich habe eine Array pTeilloesung mit z.B. {1;3;0;0} (Positionen von Damen auf einem 4x4 Schachfeld; Damenproblem). Die Array lTeilloesung soll nun diese Informationen übernehmen, aber leicht abgewandelt:

Code: Alles auswählen

lTeilloesung := zurueck(pTeilloesung);
lTeilloesung ist danach auch richtiger Weise {1;4;0;0}. Allerdings nimmt auch pTeilloesung diesen Wert an.
Wie verhindere ich, dass pTeilloesung den Wert ändert? Oder anders gesagt: Wie kann ich der function zurueck eine Kopie von pTeilloesung und keinen Zeiger auf pTeilloesung übergeben?
Gruß

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

Re: Parameter soll Kopie statt Zeiger übergeben

Beitrag von Michl »

So kann man Dir nicht wirklich antworten, dazu müsstest Du etwas Quelltext beifügen.

Es kann sein, dass Du in Deiner Funktion "Zurueck" evtl. den Pointer lTeilloesung mit pTeilloesung überschreibst. Genauso kann es sein, dass schon Deine Function "Zureck" vom Typ Pionter ist, dann überschreibst Du direkt den Pointer.

Also bitte etwas mehr Quelltext posten.

Michl

Code: Alles auswählen

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

maeries
Beiträge: 6
Registriert: Fr 8. Mär 2013, 17:39

Re: Parameter soll Kopie statt Zeiger übergeben

Beitrag von maeries »

Code: Alles auswählen

function tAlgorithmus.zurueck(pTeilloesung : TArray) : TArray;
var
  lX : longint;
  lTeilloesung : TArray;
  lOldLength : longint;
begin
  setLength(lTeilloesung, zSize-1);
  lTeilloesung := pTeilloesung;
  repeat
    lX := lTeilloesung[Laenge(lTeilloesung)];
    lOldLength := Laenge(lTeilloesung);
    lTeilloesung[Laenge(lTeilloesung)]:= 0;
    lTeilloesung := DameHinzufuegen(lTeilloesung, lX+1);
  until (lOldLength = Laenge(lTeilloesung)) OR (Laenge(lTeilloesung) = -1);
  if Laenge(lTeilloesung) > 0 then
    result := lTeilloesung
  else
    result := pTeilloesung;
end;

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

Re: Parameter soll Kopie statt Zeiger übergeben

Beitrag von Michl »

Das Problem ist, dass Du die Adresse von Deinem neu erstellten TArray überschreibst:

Code: Alles auswählen

 
 lTeilloesung := pTeilloesung;
Somit beziehen sich alle nachfolgenden Zuweisungen egal ob lTeilloesung oder pTeilloesung immer auf den ein und den selben Pointer und somit dem identischen TArray. Daher muss Du in Deiner Function nicht die Adressen sondern die Inhalte, also alle Werte hinter den Pointern von pTeilloesung.arr[ 0..3 , 0..3 ] kopieren!

Evtl. wäre es auch sinnvoller bei diesem Projekt keine dynamischen Arrays zu nehmen, sondern ein festes, da sich ja nicht die Anzahl der Felder ändern, sondern immer nur deren Inhalt. Soweit ich das übersehen kann.

Code: Alles auswählen

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

maeries
Beiträge: 6
Registriert: Fr 8. Mär 2013, 17:39

Re: Parameter soll Kopie statt Zeiger übergeben

Beitrag von maeries »

Also die Arrays müssen dynamisch sein, da man vorher im Formular die Größe eingibt.
Wie muss die Zeile

Code: Alles auswählen

lTeilloesung := pTeilloesung;
denn dann heißen? Bei

Code: Alles auswählen

pTeilloesung.arr[ 0..3 , 0..3 ]
bekomme ich immer "Illegal qualifier" ausgegeben.

Benutzeravatar
theo
Beiträge: 10899
Registriert: Mo 11. Sep 2006, 19:01

Re: Parameter soll Kopie statt Zeiger übergeben

Beitrag von theo »

Bist du sicher, dass du so eine merkwürdige TArray Klasse brauchst? Würde ein normales statisches oder dynamisches Array nicht reichen?

Lies doch mal hier: http://www.delphi-treff.de/tutorials/ob ... rs/arrays/

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

Re: Parameter soll Kopie statt Zeiger übergeben

Beitrag von Michl »

Theo war schneller, ich denke ebenfalls, dass dafür ein normales dynamisches Array ausreicht also z.B: >i : array of integer<

habe mal ein kleines Beispiel angehangen.

Solltest Du wirklich mit TArray weiterarbeiten müssen/wollen, dann müsstest Du alle Werte, die hinter den Pointern des TArray stehen einzeln kopieren (das meinte ich mit >pTeilloesung.arr[ 0..3 , 0..3 ]<):

z.B.

Code: Alles auswählen

 
var 
  x,y:integer;
  si1,si2:^shortint;
...
for y:=0 to feldhoehe-1 do
  for x:=0 to feldbreite-1 do
     begin  
       si1:=pTeilloesung.arr[x,y];
       si2:=lTeilloesung.arr[x,y];
       si2^:=si1^;
     end;
 
Dateianhänge
Project1.ZIP
(126.6 KiB) 70-mal heruntergeladen

Code: Alles auswählen

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

maeries
Beiträge: 6
Registriert: Fr 8. Mär 2013, 17:39

Re: Parameter soll Kopie statt Zeiger übergeben

Beitrag von maeries »

Also ich will nicht und muss nicht mit TArray arbeiten, wenn ich aber TArray durch array of integer ersetze, bekomme ich wenn ich eine function vom Typ array of integer erstelle ( z.B. function DameHinzufuegen (pTeilloesung : array of longint; pX : longint) : array of longint; ) immer die Meldungen: "Type identifier expected" und "Syntax error, ";" expected but "array" found". Wenn ich jedoch einen Parameter vom typ Array of integer erstelle geht das. Wieso und was kann man dagegen machen?

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

Re: Parameter soll Kopie statt Zeiger übergeben

Beitrag von Michl »

Das liegt daran, dass Du bei Deiner Function >zurueck< einen Zeiger(Pointer) als Rückgabewert lieferst, nicht die Liste Deiner Damen an sich. Also nur die Stelle im Speicher, wo sich Deine Damen befinden!
Eine Funktion benötigt als Rückgabewert immer einen eindeutig in der Größe definierten Rückgabewert! Daher funktioniert >array of integer< nicht!

Du kannst Dein global definiertes >array of integer< in den Funktionen ändern,
Du kannst wie zuvor geschrieben die Werte, die sich hinter den Zeigern Deines TArrays befinden ändern,
Du kannst Dein Spielfeld in der Größe maximal begrenzen und dann diese maximale Größe als Funktionresult nehmen

Ich würde bei Deinem Fall Variante 1 bevorzugen!

Code: Alles auswählen

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

maeries
Beiträge: 6
Registriert: Fr 8. Mär 2013, 17:39

Re: Parameter soll Kopie statt Zeiger übergeben

Beitrag von maeries »

Wie meinst du das bei Variante 1?

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

Re: Parameter soll Kopie statt Zeiger übergeben

Beitrag von Michl »

Es ist echt schwer Dir zu antworten, ohne genau zu wissen, was Du eigentlich bezweckst! Ich spekulier daher mal ins Blaue hinein:

Du hast ein Spielfeld, was Du variabel ändern kannst. Dazu hast Du bisher global ein TArray definiert.

Wenn Du anstatt diesem TArray eine Variabel vom Typ >array of interger< oder einem anderem Typ nimmst, kannst Du ihn direkt bearbeiten. Dann benötigst Du auch keine Funktion, die Dir einen Rückgabewert liefert, dies kann dann mit Hilfe einer Procedure erfolgen.

Ich versuche mal Deinen Code nachzubilden:

Code: Alles auswählen

procedure tAlgorithmus.Neue_Dame;
var
  lX : longint;
  i : integer;
  lTeilloesung : Array of Integer;
begin
//  setlength(lTeilloesung, length(pTeilloesung));         //Länge des global definierten Arrays übernehmen
//  for i:=0 to high(lTeilloesung) do lTeilloesung[i]:=0;  //Werte in Array schreiben, damit nicht "irgendwas" darin steht oder
  lTeilloesung:=copy(pTeilloesung);                      //Werte von globalen Array (pTeilloesung) übernehmen
  repeat
    lX := lTeilloesung[high(lTeilloesung)];              //was Du mit dem letzten Array-Wert bezweckst, ist mir unklar
    lTeilloesung[high(lTeilloesung)]:= 0;
    lTeilloesung := DameHinzufuegen(lTeilloesung, lX+1); //ebenso, was dann hier geschieht
  until (length(lTeilloesung)=length(pTeilloesung)) or (length(lTeilloesung)=0);
  if length(lTeilloesung)>0 then begin                   //Nur wenn lTeilloesung die gleiche Anzahl der Felder hat, den globalen pTeilloesung-Array füllen
                                                         //Dies wird aber schon beim ersten Durchlauf der Schleife passieren, da setlength dies zuvor gemacht hat
    pTeilloesung:=copy(lTeilloesung);
  end;
end;
Evtl. so???
Zuletzt geändert von Michl am So 10. Mär 2013, 00:09, insgesamt 1-mal geändert.

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 »

maeries hat geschrieben:Also die Arrays müssen dynamisch sein,
Dynamische Arrays werden immer als Zeiger übergeben. Statische Arrays dagegen als Wert.

Finde ich auch für den Anwender nicht sehr "offensichtlich".

Ganz schwierig ist, dass ein dynamisches Array als Funktionswert auch in Wirklichkeit ein als Zeiger übergebener Parameter ist und deshalb dann immer noch die alten Werte enthält, wenn die Funktion nicht explizit die Größe auf 0 setzt oder alle Werte überschreibt. Ich glaube, die Funktion kann sogar aus "Result" die "übergebenen" Werte auslesen. (Habe ich in Delphi vor längerer Zeit 'mal ausprobiert, mit fpc noch nicht)

-Michael

maeries
Beiträge: 6
Registriert: Fr 8. Mär 2013, 17:39

Re: Parameter soll Kopie statt Zeiger übergeben

Beitrag von maeries »

@Michi: So würde es natürlich auch gehen. Allerdings will ich nicht nur dieses Programm fertigstellen, sondern auch was lernen. Ich hatte dieses Problem schon einmal. Da lies es sich ganz einfach umgehen und hier ist es auch mit einem workaround machbar, aber wer weiß, wie das in Zukunft aussehen wird.

Ich habe mal gehört, dass man, wenn man per Parameter keinen Wert sondern einen Zeiger übergeben will, dann vor dem Parameter ein Var schreibt (procedure xy (var parameter : integer)). Ich hatte gehofft, dass es etwas Ähnliches für die andere Richtung gibt.

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

Re: Parameter soll Kopie statt Zeiger übergeben

Beitrag von Michl »

Aber was nützt Dir denn der Zeiger?! Du willst doch Dein Spielfeld (den Inhalt von Deinem dynamischen Array) verändern und nicht die Speicherorte!!!

Ein dynamisches Array ansich (sollte besser heißen: die Werte des dynamischen Arrays) kann man nicht per Function zurückgeben, da die Dimension des Arrays nicht bekannt ist! Ich kann Dir nur nochmal empfehlen die Seite, die von Theo verlinkt wurde zu lesen, da sind dynamische Arrays sehr gut beschrieben!

[EDIT] Ein dynamisches Array kann man per Function zurückgeben

[EDIT2] Es wird allerdings der Pointer der Function zurückgegeben, was in diesem Fall dann nicht die Lösung darstellt, da nicht die Werte zurückgegeben werden!

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. Also wird direkt der Parameter genutzt! Ohne dem >var< wird eine Kopie des Parameters erstellt, das Original bleibt erhalten, nach Beendigung der Procedure/Function wird die Kopie des Parameters wieder gelöscht (im Speicher freigegeben).
Der Zeiger/Pointer/Referenz zeigt eigentlich nur auf eine Stelle im Speicher. Daher geht dies umgekehrt nicht, da nicht definiert ist, was sich an der Stelle im Speicher befindet. Dies könenn Werte wie Boolean, Byte, Integer, String oder sonstwas sein, dass weiss der Compiler nicht, das ist die Aufgabe des Programmierers, das dem Compiler zu sagen.
Zuletzt geändert von Michl am Mi 13. Mär 2013, 10:35, insgesamt 4-mal geändert.

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:Ein dynamisches Array ansich kann man nicht per Function zurückgeben...
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;
Das folgende geht in fpc aber nicht. In Delphi geht es, Keine Ahnung was besser ist:

Code: Alles auswählen

 
type
  tdyn = array of integer;
function xy: tdyn;
begin
 result[1] := 77;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
  x: tdyn;
begin
 SetLength(x, 10);
 x := xy();
 caption := IntToStr(x[1]);
end;
in Delphi geht sogar:

Code: Alles auswählen

type
  tdyn = array of integer;
function xx: tdyn;
begin
 result[1] := result[2];
end;
procedure TForm1.FormCreate(Sender: TObject);
var
  x: tdyn;
begin
 SetLength(x, 10);
 x[2] := 88;
 x := xx();
 caption := IntToStr(x[1]);
end;

-Michael
Zuletzt geändert von mschnell am So 10. Mär 2013, 15:06, insgesamt 2-mal geändert.

Antworten