Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Adresse eines dynamischen typisierten Arrays als Parameter (https://www.delphipraxis.net/40076-adresse-eines-dynamischen-typisierten-arrays-als-parameter.html)

Thomas233 10. Feb 2005 17:06


Adresse eines dynamischen typisierten Arrays als Parameter
 
Guten Abend,

ich würde gerne ein dynamisches Array als Parameter übergeben.
Was mir Schwierigkeiten bereitet ist die Tatsache das ich nicht will das die Funktion der das Array übergeben wird mit einer Kopie des übergebenen Arrays arbeitet (also das Array nochmals am Stack anlegt wird und dann deren Inhalte ins neue Array kopiert werden) da dies bei größeren Arrays ein zu großer zeitlicher Aufwand wäre.
Am optimalsten wäre es die Startadresse des dynamischen Arrays als Parameter zu übergeben und dann in der Funktion mit der Startadresse zu arbeiten (wobei es schon recht toll wäre mit einem typisierten Pointer zu arbeiten). In C geht das eben mit typisierten Pointern.

Wie mache ich das in Delphi?

Vielen Dank im Vorraus für eure Hilfe !

Mit freundlichen Grüßen,
Thomas

dizzy 10. Feb 2005 17:10

Re: Adresse eines dynamischen typisierten Arrays als Paramet
 
Mit eine var-Parameter wird automatisch die Adresse übergeben, und keine Kopie angelegt.
Delphi-Quellcode:
procedure(var DeinArray: TDeinArrayTyp);
begin
.
.
.
end;
Du musst dir nur einen Type für das dyn. Array machen
Delphi-Quellcode:
type
  TDeinArrayTyp = array of Integer;
Der übergebene Parameter muss dann auch vom Typ TDeinArrayTyp sein.

Gruss,
Fabian

Thomas233 10. Feb 2005 17:12

Re: Adresse eines dynamischen typisierten Arrays als Paramet
 
Hi Fabian,

danke für die rasche Antwort.

Aber: Wird so nicht das Array bei der Übergabe (in den Stack) kopiert ?

Mit freundlichen Grüßen,
Thomas Tschofenig

dizzy 10. Feb 2005 17:16

Re: Adresse eines dynamischen typisierten Arrays als Paramet
 
Nope, durch Angabe des Schlüsselwortes "var" wird vom Compiler nur ein Pointer auf das Array übergeben. Du brauchst dich aber, anders als bei C, nicht in der procedure drum zu kümmern; du kannst alles so schreiben wie mit für Kopien. Das ist imho das hauptsächliche was Delphi im Vergleich zu C so schön und lesbar macht :)

\\edit: Es ist also dann ein Call-by-Reference, während ohne "var" ein Call-by-Value gemacht wird.

Thomas233 10. Feb 2005 17:25

Re: Adresse eines dynamischen typisierten Arrays als Paramet
 
Oh, sorry, das hatte ich nicht gewusst.

Und wenn ich jetzt z.b. eine Basisklasse habe und ich will der Funktion aber eine Abwandlung der Basisklasse übergeben (bzw. ein Array vom Typ der abgewandelten Klasse) geht das dann auch wenn man nur ein Array vom Typ der Basisklasse deklariert hat (in der Unit wo die Funktion drinnen ist) ?

Vielen Dank !

Mit freundlichen Grüßen,
Thomas Tschofenig

dizzy 10. Feb 2005 17:34

Re: Adresse eines dynamischen typisierten Arrays als Paramet
 
Ich schreibe mal, wei ich das jetzt verstanden habe :)

Delphi-Quellcode:
type
  TBaseClass = class(TObject);
  .
  .
  end;
 
  TDerived = class(TBaseClass);
  .
  .
  end;

  TMyArray = array of TBaseClass;
end;

.
.

procedure Foo(var v: TMyArray);
begin
.
.
end;

var
  arr: TMyArray;
begin
  SetLength(arr, 1);
  arr[0] := TDerived.Create;
  Foo(arr);
end;
So ginge das. Allerdings hat das weniger mit Arrays an sich zu tun, als viel mehr mit OOP. Das hat auch zur Folge, dass du über "arr" nur die Felder der Klassen erreichst, die auch in TBaseClass deklariert sind. Ansonsten musst du casten: TDerived(arr[0]).FunktionInDerived;

Ich hoffe ich hab das jetzt richtig verstanden :)

Gruss,
Fabian

Chewie 10. Feb 2005 17:38

Re: Adresse eines dynamischen typisierten Arrays als Paramet
 
Bei dynamischen Arrays kann man sich in diesem Fall die var-Angabe sparen, da dynamische Arrays von sich aus Referenztypen sind, ähnlich wie die C-Arrays (nur dass diese u.U. auf dem Stack liegen, Delphi dyn. Arrays aber immer auf dem Heap).

Thomas233 10. Feb 2005 17:50

Re: Adresse eines dynamischen typisierten Arrays als Paramet
 
Hi dizzy,

fast ;-)

Delphi-Quellcode:
type
  TBaseClass = class(TObject); //In BaseClass.pas deklariert
  .
  .
  end;
 
  TDerived = class(TBaseClass); //In DerivedClass.pas deklariert
  .
  .
  end;

  TMyArray1 = array of TBaseClass; // Vorsicht: Ist deklariert in der Unit in welcher die Funktion "Foo" vorkommt, in Unit1.pas
  TMyArray2 = array of TDerived; // Ist deklariert in der Unit in der die Funktion "Foo" aufgerufen wird, also in Unit2.pas
end;

.
.

// In Unit1.pas
procedure Foo(var v: TMyArray1);
begin
.
.
end;

// In Unit2.pas
procuedre TuWas;
var
  arr: TMyArray2;
begin
  SetLength(arr, 1);
  Foo(arr);
end;
uses-Klausel von Unit1.pas:
BaseClass.pas

uses-Klausel von Unit2.pas:
DerivedClass.pas
Unit1.pas

Ich schätze mal das geht so nicht, oder?

Mit freundlichen Grüßen,
Thomas Tschofenig

sniper_w 10. Feb 2005 18:05

Re: Adresse eines dynamischen typisierten Arrays als Paramet
 
Zitat:

Bei dynamischen Arrays kann man sich in diesem Fall die var-Angabe sparen, da dynamische Arrays von sich aus Referenztypen sind, ähnlich wie die C-Arrays (nur dass diese u.U. auf dem Stack liegen, Delphi dyn. Arrays aber immer auf dem Heap)
Was ist Heap?

Oxmyx 10. Feb 2005 18:14

Re: Adresse eines dynamischen typisierten Arrays als Paramet
 
Zitat:

Zitat von dizzy
Nope, durch Angabe des Schlüsselwortes "var" wird vom Compiler nur ein Pointer auf das Array übergeben. Du brauchst dich aber, anders als bei C, nicht in der procedure drum zu kümmern; du kannst alles so schreiben wie mit für Kopien. Das ist imho das hauptsächliche was Delphi im Vergleich zu C so schön und lesbar macht :)

\\edit: Es ist also dann ein Call-by-Reference, während ohne "var" ein Call-by-Value gemacht wird.

Äh, sicher? Mit dem Schlüsselwort var wird doch überhaupt kein Wert mehr übergeben (auch kein Zeiger), da es ja eine Referenz darstellt. D.h. es wird kein neuer Zeiger auf dem Stack angelegt, der auf das Array zeigt, sondern die Funktion "kennt" das ursprüngliche Array einfach.

dizzy 10. Feb 2005 18:16

Re: Adresse eines dynamischen typisierten Arrays als Paramet
 
Zitat:

Zitat von Thomas233
Ich schätze mal das geht so nicht, oder?

Korrekt. Hab ich befürchtet dass du das so meintest ;). Der Typ der übergebenen Variable muss mit dem im Prozedurkopf übereinstimmen. Das geht sogar so weit, dass ein und die selbe Deklatation in 2 unterschiedlichen Units nicht den selben Typ darstellen!

Bei deinem Vorhaben würdest du glaub ich um untypisierte Pointer nicht umher kommen. Dann wird das ganze ähnlich unschön wie in C :D
Wird auch viel Casterei...

@Chewie: Stümmt ja... Fixe Arrays, Records und generische Typen sind per Standard Call-by-Value, und Dyn. Arrays und Objekte sind immer Call-by-Reference.
Wie es bei Records aussieht, die Objekte und dyn. Arrays beinhalten aussieht weiss ich es nicht genau. Das gibt u.U. ein heiloses Durcheinander :).

Zusätzlich werden Objekte und dyn. Arrays immer im Heap angelegt, und lok. Variablen und fixe Arrays gewöhnlich auf dem Stack.

@sniper_w: Bitte einen eigenen Thread auf machen ;)

dizzy 10. Feb 2005 18:18

Re: Adresse eines dynamischen typisierten Arrays als Paramet
 
Zitat:

Zitat von Oxmyx
Zitat:

Zitat von dizzy
Nope, durch Angabe des Schlüsselwortes "var" wird vom Compiler nur ein Pointer auf das Array übergeben. Du brauchst dich aber, anders als bei C, nicht in der procedure drum zu kümmern; du kannst alles so schreiben wie mit für Kopien. Das ist imho das hauptsächliche was Delphi im Vergleich zu C so schön und lesbar macht :)

\\edit: Es ist also dann ein Call-by-Reference, während ohne "var" ein Call-by-Value gemacht wird.

Äh, sicher? Mit dem Schlüsselwort var wird doch überhaupt kein Wert mehr übergeben (auch kein Zeiger), da es ja eine Referenz darstellt. D.h. es wird kein neuer Zeiger auf dem Stack angelegt, der auf das Array zeigt, sondern die Funktion "kennt" das ursprüngliche Array einfach.

Na, du musst doch aber die Adresse zumindest in einem Register hinterlegen. Weil "einfach so" kennt die das Array bestimmt nicht, wär ja nen Dingen ;)
Einen ganz neuen Zeiger wird man, je nach Parameteranzahl, wohl nicht benötigen.
\\edit: Die ersten 3 Paramenter werden imho in Delphi via Register übergeben (wenn möglich), und alle weiteren werden auf den Stack gepackt. Und ein Pointer ist ja im Grunde erstmal nix anderes als ein Integer.

Chewie 10. Feb 2005 18:34

Re: Adresse eines dynamischen typisierten Arrays als Paramet
 
Zitat:

Zitat von dizzy
Wie es bei Records aussieht, die Objekte und dyn. Arrays beinhalten aussieht weiss ich es nicht genau. Das gibt u.U. ein heiloses Durcheinander :)

Eigentlich nicht ;) Du darfst immer nur die benachbarte Indirektionsebene betrachten. Ein dyn. array liegt erstmal auf dem Heap. Dort sind die Daten, die wiederum Zeiger auf woanders hin sein können etc.
Das ist kein Durcheinander, sondern eine logische Hierarchie - man muss nur vorne anfangen ;)

Thomas233 10. Feb 2005 19:27

Re: Adresse eines dynamischen typisierten Arrays als Paramet
 
Hi @ all,

um nochmal auf das obige Beispiel zurück zu kommen, ohne das Array habe ich es durchaus geschafft eine abgewandelte Klasse anstatt einer Basisklasse einer Funktion als Parameter zu übergeben obwohl in der Deklaration der Funktion die Basisklasse verlangt war und zwar so:
Delphi-Quellcode:
type
    PMyClass=^TBasicClass;

procedure Foo(Class:PMyClass)
begin
.....
end;

procedure Tuwas;
var MyClass:TDerivedClass;
begin
Foo(@MyClass);
end;
Ich weiss zwar nicht so genau warum es so ging, aber es ging immerhin ;-)

Mit freundlichen Grüßen,
Thomas Tschofenig

Sprint 10. Feb 2005 19:32

Re: Adresse eines dynamischen typisierten Arrays als Paramet
 
Aus objektorientierter Sicht, benutzt man zum Verwalten von Klassen kein Array, sondern TObjectList. TList kannst du natürlich auch nehmen.

Thomas233 10. Feb 2005 19:39

Re: Adresse eines dynamischen typisierten Arrays als Paramet
 
Hi Sprint,

da hast du sicherlich recht, aber dann müssen die ganze Zeit wieder die Typecasts herhalten wenn ich mehrere Klassen in einer Liste habe :-(

Mit freundlichen Grüßen,
Thomas Tschofenig

Sprint 10. Feb 2005 19:44

Re: Adresse eines dynamischen typisierten Arrays als Paramet
 
Zitat:

Zitat von Thomas233
da hast du sicherlich recht, aber dann müssen die ganze Zeit wieder die Typecasts herhalten wenn ich mehrere Klassen in einer Liste habe

Aber nicht wenn deine Klasse von TObject abgeleitet ist und du TObjectList verwendest.


Edit: \\ Sorry. Natürlich musst du ein Typcast machen.

dizzy 10. Feb 2005 19:54

Re: Adresse eines dynamischen typisierten Arrays als Paramet
 
Zitat:

Zitat von Thomas233
Hi @ all,

um nochmal auf das obige Beispiel zurück zu kommen, ohne das Array habe ich es durchaus geschafft eine abgewandelte Klasse anstatt einer Basisklasse einer Funktion als Parameter zu übergeben obwohl in der Deklaration der Funktion die Basisklasse verlangt war und zwar so:
Delphi-Quellcode:
type
    PMyClass=^TBasicClass;

procedure Foo(Class:PMyClass)
begin
.....
end;

procedure Tuwas;
var MyClass:TDerivedClass;
begin
Foo(@MyClass);
end;
Ich weiss zwar nicht so genau warum es so ging, aber es ging immerhin ;-)

Mit freundlichen Grüßen,
Thomas Tschofenig

Klar, das geht. Das "Gehampel" mit dem Pointer könntest du dir sogar sparen.
Delphi-Quellcode:
procedure Foo(Class: TBasicClass);
begin
...
end;

procedure Tuwas;
var MyClass:TDerivedClass;
begin
  Foo(MyClass);
end;
Geht genau so, und das deshalb, weil in Delphi ohnehin alle Objekte Pointer in den Heap sind, und das weiss der Compiler, und macht es dir deshalb einfach/lesbarer :)

Aber bei der Sache mit dem Array übergibst du ja keine Variable vom Typ "Klasse", sondern "Array". Ein Array weiss aber rein garnichts von Ableitung o.ä., und schon garnichts darüber was es im einzelnen für Werte beinhaltet. Für den Compiler sind das dann grundverschiedene Typen, da das eine Array ja nicht vom anderen Abgeleitet ist/sein kann. Was in den Arrays steht ist ihm wurscht ;).

TList mit etwas Casterei (lässt sich mit ein paar Hilfsvariablen der möglichen Typen evtl. etwas einschränken) ist aber, wie Sprint richtigerweise sagt, "runder". Das denke ich auch.

Gruss,
Fabian

Thomas233 11. Feb 2005 12:28

Re: Adresse eines dynamischen typisierten Arrays als Paramet
 
Hi Fabian,

sicherlich ist es auch in meinem Fall besser TObjectlist oder änhliches zu verwenden, ich würds ja auch gerne verwenden, nur die Typcasterei geht mir auf die Nerven.

Gibt nicht eine Möglichkeit mit der man sowas wie den folgenden Block vereinfachen könnte ??

Delphi-Quellcode:
if Class is TBaseClass then
   (Class as TBaseClass).Tuwas
else if Class is TDerivedClass then
   (Class as TDerivedClass).Tuwas;
Liebe Grüße und vielen Dank,
Thomas

dizzy 11. Feb 2005 20:34

Re: Adresse eines dynamischen typisierten Arrays als Paramet
 
Naja, so richtig nicht. Man kann es in der Laufzeit etwas optimieren, da der "as"-Operator nochmals eine "is"-Prüfung macht. So wie du es machst, bist du zum Zeitpunkts des Casts ja schon sicher die richtige Klasse in der Hand zu haben -> direkt casten:
Delphi-Quellcode:
if Class is TBaseClass then
   TBaseClass(Class).Tuwas
else if Class is TDerivedClass then
   TDerivedClass(Class).Tuwas;
Ich hab mir, wenn ich sehr viel so zugreifen musste, immer für jeden Typ eine lokale Variable angelegt, und dieser dann das gefundene zugewiesen. Dann kann man mit der ja weiter machen. Für so kurze Dinger wie oben gibt es imho keinen komfortableren Weg. (Wäre aber auch daran interessiert :))

Gruss,
Fabian

Thomas233 15. Feb 2005 11:42

Re: Adresse eines dynamischen typisierten Arrays als Paramet
 
So, ich hab jetzt alle Arrays auf TList-Klassen umgestellt und verwalte die ganzen Pointer ab sofort in den TList-Klassen.
Durch den selben Trick den ich auch schon bei den Arrays angewendet habe, ich deklariere bei der Übergabe eines Parameters an eine Funktion einen Zeiger auf eine Basisklasse übergebe dann aber einen Zeiger auf eine von der Basisklasse abgeleitete Klasse, brauche ich mich gar nicht mehr mit der Casterei herumschlagen. Ist zwar nicht sonderlich objektorientiert, aber immerhin habe ich mir dadurch einigen Aufwand erspart.

Jetzt stellt sich mir nur das folgende Problem:
Da ich bei dem erzeugen einer neuen Klasse diese nicht lokal anlegen darf sondern nur global kann ich bei der Erzeugung nicht ohne Pointer arbeiten.
Ich deklariere nun also einen Pointer auf eine Klasse welcher sich lokal in einer Funktion befindet. Wie muss ich es nun anstellen das der Pointer auf einen für die Klasse reservierten Speicherbereich zeigt und die Klasse auch initialisiert wurde (d.h. der Konstruktor müsste auch aufgerufen werden) ?

Vielen Dank im Vorraus !

Liebe Grüße,
Thomas

dizzy 15. Feb 2005 12:11

Re: Adresse eines dynamischen typisierten Arrays als Paramet
 
Also im Grunde so etwas: (?)
Delphi-Quellcode:
procedure GenerateInstance(Liste: TList);
var
  p: ^TDeineKlasse;
begin
  p^ := TDeineKlasse.Create;
  Liste.Add(Pointer(p));
end;
(in etwa - kann leicht anders sein...)

Du brauchst an sich dafür keinen lokalen Pointer, sondern kannst die Instanzerzeugung im Add machen:
Delphi-Quellcode:
procedure GenerateInstance(Liste: TList);
begin
  Liste.Add(TDeineKlasse.Create);
end;
Sinn kann es aber dann machen, wenn du vor dem Hinzufügen zur Liste noch mit der Instanz hantierst.

Thomas233 15. Feb 2005 19:39

Re: Adresse eines dynamischen typisierten Arrays als Paramet
 
Hmm....klappt nicht:

Delphi-Quellcode:
var Model:^TD3DXmodel;

Model^:=TD3DXModel.Create(D3DEngine.D3d9Device);

Model^.Tuwas;
//Zugriffsverletzung !
Vielen Dank im Vorraus !

Liebe Grüße,
Thomas

Chewie 15. Feb 2005 20:07

Re: Adresse eines dynamischen typisierten Arrays als Paramet
 
Klar geht das nicht, du musst dir noch Speicher für deine Objektreferenz anfordern (mit New).

Thomas233 15. Feb 2005 21:37

Re: Adresse eines dynamischen typisierten Arrays als Paramet
 
Achso, danke, genau DAS wollte ich wissen.

Thx nochmal !

Liebe Grüße,
Thomas


Alle Zeitangaben in WEZ +1. Es ist jetzt 10:39 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