Pointer Zuweisungen - Umweg notwendig

Für Fehler in Lazarus, um diese von anderen verifizieren zu lassen.
MAC
Beiträge: 770
Registriert: Sa 21. Feb 2009, 13:46
OS, Lazarus, FPC: Windows 7 (L 1.3 Built 43666 FPC 2.6.2)
CPU-Target: 32Bit

Pointer Zuweisungen - Umweg notwendig

Beitrag von MAC »

Hallo
ich habe gerade als ich an einem OpenGl projekt Gearbeitet habe ein merkwürdiges Verhalten von Pointern festgestellt. (keine OpenGL kentnisse notwendig.)

Ich habe folgenden Quellcode

Code: Alles auswählen

PTOctTree = ^TOctTree;
 
  TOctTreeNode = class
     Trees: array [0..1,0..1,0..1] of PTOctTree;
     procedure Start;
  end;
 
  TOctTree = class
    //... unimportant informations...
    node:TOctTreeNode; // if node = nil then last OctTree
  end;
Jetzt will ich bei der Start funktion 8 TOctTrees erzeugen und diese in den TreeNode einfügen. (Grob zur Materie. Man hat einen Knoten, an dem sitzen 8 Kästchen. In der mitte von jedem dieser Kästchen ist wieder ein Knoten, daran sitzen wieder 8 Kästchen. Das kann man jetzt bis zum gehtnichtmehr weiter machen)

Meine Start procedure sieht so aus

Code: Alles auswählen

procedure TOctTreeNode.Start;
var
   x,y,z:integer;
   puf:TOctTree;
begin
for x := 0 to 1 do
    begin
    for y := 0 to 1 do
        begin
        for z := 0 to 1 do
            begin
            puf := TOctTree.Create;
            Trees[x,y,z] := @puf;
            end;
        end;
    end;
end;
Komicher weise ist folgendes nicht möglich

Code: Alles auswählen

procedure TOctTreeNode.Start;
var
   x,y,z:integer;
begin
for x := 0 to 1 do
    begin
    for y := 0 to 1 do
        begin
        for z := 0 to 1 do
            begin
           // Tree wird direkt zugewiesen
            Trees[x,y,z] := @TOctTree.Create;
            end;
        end;
    end;
end;

Code: Alles auswählen

Error: Incompatible types: got "<address of function:TObject of object;Register>" expected "PTOctTree"
Ist jetzt kein Weltuntergang, aber woran könnte das liegen ?
Lazarus 0.9.29
SVN 28808
FPC 2.4.3
Datum 23.12.10
Windows XP SP3

Code: Alles auswählen

Signatur := nil;

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

Re: Pointer Zuweisungen - Umweg notwendig

Beitrag von theo »

Mal ne andere Frage, wieso machst du nicht einfach

Trees: array [0..1,0..1,0..1] of TOctTree;

Also nicht den Zeiger auf den Zeiger?

Bei deiner Verwendung kriegst du einen Zeiger auf die Methode Create, nicht auf das Objekt.

MAC
Beiträge: 770
Registriert: Sa 21. Feb 2009, 13:46
OS, Lazarus, FPC: Windows 7 (L 1.3 Built 43666 FPC 2.6.2)
CPU-Target: 32Bit

Re: Pointer Zuweisungen - Umweg notwendig

Beitrag von MAC »

:oops: stimmt, das mit der Verweisung auf Create hab ich gar nicht beachtet.

Wenn ich das folgendermaßen schreibe, dann mekert der (bei //hier) das er TOctTree nicht kennt. Gibt es da ne möglichkeit dem das zu sagen das das später kommt ? Umdrehen kann mans nicht, weil sonnst das gleiche Spiel mit TOctTreeNode kommt.

Code: Alles auswählen

TOctTreeNode = class
     Trees: array [0..1,0..1,0..1] of TOctTree; //Hier
     procedure Start;
  end;
 
  TOctTree = class
    node:TOctTreeNode; // if node = nil then last OctTree
  end;
Danke

Code: Alles auswählen

Signatur := nil;

am2
Lazarusforum e. V.
Beiträge: 116
Registriert: Di 21. Dez 2010, 09:59
OS, Lazarus, FPC: Win (L 0.9.26 beta FPC 2.2.2)
CPU-Target: 32 Bit

Re: Pointer Zuweisungen - Umweg notwendig

Beitrag von am2 »

[quote="MAC"]Hallo
Meine Start procedure sieht so aus

Code: Alles auswählen

procedure TOctTreeNode.Start;
var
   x,y,z:integer;
   puf:TOctTree;
begin
for x := 0 to 1 do
    begin
    for y := 0 to 1 do
        begin
        for z := 0 to 1 do
            begin
            puf := TOctTree.Create;
            Trees[x,y,z] := @puf;
            end;
        end;
    end;
end;

Code: Alles auswählen

puf := TOctTree.Create;
            Trees[x,y,z] := @puf;
Das macht mich stutzig: In Delphi sind alle Objekte prinzipiell Zeiger, auch wenn man es denen nicht mehr ansieht. puf wäre demnach ein Zeiger auf einen TOktTree. Die Adresse von puf sollte irgendwo auf dem Stack liegen. Wenn Du nun Trees[x,y,z] diese Adresse zuweist, dann sollten eigentlich alle 8 Zeigerlein auf die gleiche Stelle im Stack zeigen. Irre ich mich? Falls ja, wie könnte ich denn einer Variablen die Adresse von puf zuweisen, wenn nicht so?

MAC
Beiträge: 770
Registriert: Sa 21. Feb 2009, 13:46
OS, Lazarus, FPC: Windows 7 (L 1.3 Built 43666 FPC 2.6.2)
CPU-Target: 32Bit

Re: Pointer Zuweisungen - Umweg notwendig

Beitrag von MAC »

Soweit ich weis ist in der variable puf auch nur ein Zeiger zum Speicherort hinterlegt.
Aber der Compiler will es eben so. (Wenn darüber nachdenk ist es wie du erwähnt hast schon irgendwie sinnlos).
Ich denke das passiert folgendermaßen:
Objekt wird erstellt.
puf wird #123456
Trees[0,0,0] wird #123456
// Trees[0,0,0] zeigt auf das gleiche wie puf
Objekt wird erstellt.
puf wird #987654
// Hier entsteht soweit ich weis ein "Speicherleck", da das alte objekt von puf ja nicht freigegeben werden soll. Das Speicherleck wird jedoch verhindert, da die Adresse immernoch in Trees [0,0,0] gespeichert wird
Trees[1,0,0] wird #987654
//.. und so weiter

Am Ende sollten in jedem Trees ein Pointer zu einem anderen Objekt stehen.

Code: Alles auswählen

Signatur := nil;

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

Re: Pointer Zuweisungen - Umweg notwendig

Beitrag von theo »

MAC hat geschrieben: Gibt es da ne möglichkeit dem das zu sagen das das später kommt ?
Ja, einfach oberhalb erstmal nur:

TOctTreeNode = class;

deklarieren (Vorwärtsdeklaration für Klassen), dann kann man sie in der nachfolgenden Klasse verwenden.
Später weiter unten das Ganze.

MAC
Beiträge: 770
Registriert: Sa 21. Feb 2009, 13:46
OS, Lazarus, FPC: Windows 7 (L 1.3 Built 43666 FPC 2.6.2)
CPU-Target: 32Bit

Re: Pointer Zuweisungen - Umweg notwendig

Beitrag von MAC »

wieder was gelernt, danke

Code: Alles auswählen

Signatur := nil;

am2
Lazarusforum e. V.
Beiträge: 116
Registriert: Di 21. Dez 2010, 09:59
OS, Lazarus, FPC: Win (L 0.9.26 beta FPC 2.2.2)
CPU-Target: 32 Bit

Re: Pointer Zuweisungen - Umweg notwendig

Beitrag von am2 »

MAC hat geschrieben:Soweit ich weis ist in der variable puf auch nur ein Zeiger zum Speicherort hinterlegt.
Aber der Compiler will es eben so. (Wenn darüber nachdenk ist es wie du erwähnt hast schon irgendwie sinnlos).
Ich denke das passiert folgendermaßen:
Objekt wird erstellt.
puf wird #123456
Einverstanden
MAC hat geschrieben: Trees[0,0,0] wird #123456
nö, eigentlich nicht. Eigentlich müßte es jetzt auf PUF zeigen (also z.B. Stack -100)
MAC hat geschrieben: // Trees[0,0,0] zeigt auf das gleiche wie puf
Objekt wird erstellt.
eigentlich sollte das Objekt schon da sein und Puf enthält immer noch die (einzige) gültige Adresse 123456
MAC hat geschrieben: puf wird #987654
// Hier entsteht soweit ich weis ein "Speicherleck", da das alte objekt von puf ja nicht freigegeben werden soll. Das Speicherleck wird jedoch verhindert, da die Adresse immernoch in Trees [0,0,0] gespeichert wird
eben nicht. Es wird ein neues Objekt angelegt, dessen Adresse Du ebenfalls vergessen wirst, da Du im nächsten Durchlauf wieder ein neues Objekt entwirfst und es dort hin wirfst. Trees [0,0,0] .. Trees [1,1,1] zeigen ganz brav auf die Variable puf im Stack.
MAC hat geschrieben: Trees[1,0,0] wird #987654
//.. und so weiter

Am Ende sollten in jedem Trees ein Pointer zu einem anderen Objekt stehen.
M.E. nicht. PTOctTree ist ein Zeiger auf ein Objekt und damit auf einen Zeiger. Du müßtest eigentlich eine Forward- Deklaration von TOctTree machen und lediglich Trees [x,y,z]:=Puf schreiben. Wenn Du bei der Konstruktion Zeiger auf Zeiger bleiben willst, dann müsstest Du im Prinzip noch die Zeiger erzeugen, auf die Du dann zeigen kannst.

Also (pseudo) irgendetwas wie:

PufZeiger:PTOctTree;
PufObjekt:TOctTree;

PufZeiger:=New(PTOctTree);
PufZeiger^:=TOctTree.Create;
Trees[0,0,0]:=PufZeiger;

PufZeiger zeigt dann auf ein(en Zeiger auf das) Objekt, PufObjekt ist dann (der Zeiger auf) das Objekt
Und beim Zerstören mußt Du dann erst das Objekt selbst und dann den Zeiger auf dieses freigeben.

Ich denke, das willst Du nicht. Falls Dein Code - so wie er oben steht - doch funktioniert, dann lass es mich wissen. In diesem Fall wäre es mir lieb, es würde mir einer erklären, warum er funktioniert.

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Pointer Zuweisungen - Umweg notwendig

Beitrag von Socke »

Free Pascal unterstützt zwei Möglichkeiten mit Objekten zu arbeiten:

Code: Alles auswählen

type
  TMyObject = object; // TurboPascal oder irgendwo davor
  TMyClass = class; // gibts sogar schon in Delphi
var
  o1: TMyObject; 
  c1: TMyClass;
Der Unterschied: alles was mit object deklariert ist, ist ein echtes Objekt. Der Speicher wird direkt in o1 zur Verfügung gestellt. Klassen hingegen werden nur als Zeiger auf ein Objekt gespeichert. Daraus folgt auch:

Code: Alles auswählen

SizeOf(c1) = SizeOf(Pointer); // gilt immer und überall
SizeOf(o1) = TMyObject.InstanceSize;
und dass Klassen immer mit dem Constructor erzeugt werden müssen, da er den Speicher auf dem Heap reserviert.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

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

Re: Pointer Zuweisungen - Umweg notwendig

Beitrag von theo »

am2 hat geschrieben: PufZeiger:=New(PTOctTree);
PufZeiger^:=TOctTree.Create;
Ein Zeiger an sich muss nicht mit New reserviert werden. Ein Zeiger ist wie ein Cardinal oder Int64.

am2
Lazarusforum e. V.
Beiträge: 116
Registriert: Di 21. Dez 2010, 09:59
OS, Lazarus, FPC: Win (L 0.9.26 beta FPC 2.2.2)
CPU-Target: 32 Bit

Re: Pointer Zuweisungen - Umweg notwendig

Beitrag von am2 »

theo hat geschrieben:
am2 hat geschrieben: PufZeiger:=New(PTOctTree);
PufZeiger^:=TOctTree.Create;
Ein Zeiger an sich muss nicht mit New reserviert werden. Ein Zeiger ist wie ein Cardinal oder Int64.
Das ist mir schon klar, aber irgendwo muß der Speicher ja doch bereitstehen, oder? Und auch, wenn Du dynamisch boolean anlegen willst, wirst Du nicht um ein New herumkommen.

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Pointer Zuweisungen - Umweg notwendig

Beitrag von Socke »

am2 hat geschrieben:Das ist mir schon klar, aber irgendwo muß der Speicher ja doch bereitstehen, oder? Und auch, wenn Du dynamisch boolean anlegen willst, wirst Du nicht um ein New herumkommen.
Und wo legst du deinen Zeiger auf einen Zeiger ab? In einem Zeiger! Warum also nicht direkt den Zeiger in den Zeiger legen?
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

MAC
Beiträge: 770
Registriert: Sa 21. Feb 2009, 13:46
OS, Lazarus, FPC: Windows 7 (L 1.3 Built 43666 FPC 2.6.2)
CPU-Target: 32Bit

Re: Pointer Zuweisungen - Umweg notwendig

Beitrag von MAC »

am2 hat geschrieben:
MAC hat geschrieben: // Trees[0,0,0] zeigt auf das gleiche wie puf
Objekt wird erstellt.
eigentlich sollte das Objekt schon da sein und Puf enthält immer noch die (einzige) gültige Adresse 123456
Sry. Ich meinte
// Trees[0,0,0] zeigt auf das gleiche wie puf
Objekt2 wird erstellt.
Also also das Objekt gab es schon. Und nach dem // wird dann das 2te Objekt drubererstellt. Somit stimmt deins.
am2 hat geschrieben:
MAC hat geschrieben: puf wird #987654
// Hier entsteht soweit ich weis ein "Speicherleck", da das alte objekt von puf ja nicht freigegeben werden soll. Das Speicherleck wird jedoch verhindert, da die Adresse immernoch in Trees [0,0,0] gespeichert wird
eben nicht. Es wird ein neues Objekt angelegt, dessen Adresse Du ebenfalls vergessen wirst, da Du im nächsten Durchlauf wieder ein neues Objekt entwirfst und es dort hin wirfst. Trees [0,0,0] .. Trees [1,1,1] zeigen ganz brav auf die Variable puf im Stack.


Also Trees [1,1,1] = @puf?
Dazu kenn ich mich noch nicht genug mit pointern und so aus. ka.
Naja. Auf jeden Fall egal. Ich habe schon theos Methode angewand. Jetzt ist Trees array of TOctTree und nicht mehr PTOctTree.
Das ist mir schon klar, aber irgendwo muß der Speicher ja doch bereitstehen, oder? Und auch, wenn Du dynamisch boolean anlegen willst, wirst Du nicht um ein New herumkommen.
Das mit boolean ist richtig. kann ich bestätigen. Aber in diesem Fall wird durch das .Create ereigniss Ein Pointer erstellt.
(Wie du gesagt hast ein class ist in wirdklichkeit ja ein Pointer)
Dadurch wäre New doppelt gemoppelt ( vermutung von mir)

Code: Alles auswählen

Signatur := nil;

am2
Lazarusforum e. V.
Beiträge: 116
Registriert: Di 21. Dez 2010, 09:59
OS, Lazarus, FPC: Win (L 0.9.26 beta FPC 2.2.2)
CPU-Target: 32 Bit

Re: Pointer Zuweisungen - Umweg notwendig

Beitrag von am2 »

Socke hat geschrieben:
am2 hat geschrieben:Das ist mir schon klar, aber irgendwo muß der Speicher ja doch bereitstehen, oder? Und auch, wenn Du dynamisch boolean anlegen willst, wirst Du nicht um ein New herumkommen.
Und wo legst du deinen Zeiger auf einen Zeiger ab? In einem Zeiger! Warum also nicht direkt den Zeiger in den Zeiger legen?
Ich denke, Du hast da einen Denkfehler. Da ich hier nicht malen kann, muß ich versuchen, es zu beschreiben. Ich weiß aber nicht, ob das einleuchten wird.

Angenommen, ich habe ein dynamisches array [0..9] of char, dann werden auf dem Heap 10 byte reserviert und die Adresse einem Zeiger meiner Wahl zugewiesen.

Angenommen, ich habe ein dynamisches array [0..9] of pchar, dann werden auf dem Heap 10 * sizeof(POINTER) byte reserviert und die Adresse einem Zeiger meiner Wahl zugewiesen. Zu diesem Zeitpunkt kann ich aber noch kein einziges Zeicher speichern. Ich muß - so sinnlos es vielleicht klingt - 10 mal ein New für 1 Zeichen machen und mir die Adressen meiner paar Einzelbytes in den zehn Zeigervariablen speichern.

Wenn jetzt PPCHAR ein Zeiger auf PCHAR wäre, dann hätte ich mit array [0..9] of PPCHAR ein Feld für 10 Zeiger, die immer noch nicht auf ein Char, sondern eben nur auf einen weiteren Zeiger vom Typ PCHAR zeigen würden. Ich müßte also für jedes Char einen Zeiger in meinem array auf dem Heap anlegen und dann ein Byte für mein Char auf dem Heap anlegen, dessen Adresse ich dann eben diesem einen Zeiger auf dem Heap zuordnen müßte.
Du hast natürlich recht, dass ich per TypeCast erzwingen kann, dass array of PPCHAR definiert ist, ich in den Zeigern aber tatsächlich ein PCHAR abgelegt wird. Aber das kann ja nicht Sinn der Sache sein.

Und jetzt zu dieser Konstruktion
TOktNode=class
Node=array of PTOktNode
...

Node enthält ein Array von Zeigern auf Zeiger, nicht auf Objekte des Typs TOktNode. Wenn ich also das System nicht vergewaltigen will, dann müßte ich einen Zeiger auf dem Heap alloziieren, der dann seinerseits auf ein Objekt vom Typ TOktNode zeigt. Das ist mit Kanonen auf Spatzen geschossen, zugegeben, wäre aber der korrekte weg bei dieser Definition.

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Pointer Zuweisungen - Umweg notwendig

Beitrag von Socke »

Dein ursprüngliches Problem könnte syntaktisch korrekt so aussehen:

Code: Alles auswählen

Trees[x,y,z] := @TOctTree(TOctTree.Create);
am2 hat geschrieben:Angenommen, ich habe ein dynamisches array [0..9] of char, dann werden auf dem Heap 10 byte reserviert und die Adresse einem Zeiger meiner Wahl zugewiesen.
Angenommen, ich habe ein dynamisches array [0..9] of pchar, dann werden auf dem Heap 10 * sizeof(POINTER) byte reserviert und die Adresse einem Zeiger meiner Wahl zugewiesen. Zu diesem Zeitpunkt kann ich aber noch kein einziges Zeicher speichern. Ich muß - so sinnlos es vielleicht klingt - 10 mal ein New für 1 Zeichen machen und mir die Adressen meiner paar Einzelbytes in den zehn Zeigervariablen speichern.

Wenn jetzt PPCHAR ein Zeiger auf PCHAR wäre, dann hätte ich mit array [0..9] of PPCHAR ein Feld für 10 Zeiger, die immer noch nicht auf ein Char, sondern eben nur auf einen weiteren Zeiger vom Typ PCHAR zeigen würden. Ich müßte also für jedes Char einen Zeiger in meinem array auf dem Heap anlegen und dann ein Byte für mein Char auf dem Heap anlegen, dessen Adresse ich dann eben diesem einen Zeiger auf dem Heap zuordnen müßte.
Du hast natürlich recht, dass ich per TypeCast erzwingen kann, dass array of PPCHAR definiert ist, ich in den Zeigern aber tatsächlich ein PCHAR abgelegt wird. Aber das kann ja nicht Sinn der Sache sein.
Soll das heißen, die Deklaration

Code: Alles auswählen

PTOctTree = ^TOctTree;
 
  TOctTreeNode = class
     Trees: array [0..1,0..1,0..1] of PTOctTree;
     procedure Start;
  end;
 
  TOctTree = class
    //... unimportant informations...
    node:TOctTreeNode; // if node = nil then last OctTree
  end;
heißt, dass TOctTreeNode.Trees[x,y,z] als array of TOctTree interpretiert werden soll? Dann fehlt noch die Null-Terminierung. Einfacher wäre wohl

Code: Alles auswählen

Node: array[0..1,0..1,0..1] of array of TOctTree;
am2 hat geschrieben:Und jetzt zu dieser Konstruktion
TOktNode=class
Node=array of PTOktNode
...

Node enthält ein Array von Zeigern auf Zeiger, nicht auf Objekte des Typs TOktNode. Wenn ich also das System nicht vergewaltigen will, dann müßte ich einen Zeiger auf dem Heap alloziieren, der dann seinerseits auf ein Objekt vom Typ TOktNode zeigt. Das ist mit Kanonen auf Spatzen geschossen, zugegeben, wäre aber der korrekte weg bei dieser Definition.
Wenn dieses TOktNode.Node sich also wie ein PPChar nur eben mit TOktNode anstatt Char verhalten soll, würde ich die Pascal-Variante Node: array of array of TOktNode vorschlagen.
Mir ist immer noch nicht ganz klar, was du mit einem Zeiger auf eine Klasse erreichen willst.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Antworten