Pointers i Delphi

Tags:    delphi
Skrevet af Bruger #58 @ 16.06.2001
Pointers i Delphi

I denne artikel vil jeg fortælle lidt om pointers i Delphi og hvordan man kan arbejde med dem. Jeg vil komme ind på forskellige de forskellige typer pointers man kan have og hvordan man kan konvertere mellem dem.

Hvad er en pointer

En pointer er en variabel der peger på en anden variabel, altså fortæller hvor i hukommelsen den anden variabel ligger. En pointer bliver, for nu at sige det så teknisk som muligt, altid gemt som en 32 bit unsigned integer. Det betyder i praksis at man kan typecaste så meget man vil mellem pointer-typer og integer-typer, og at det ikke kan betale sig at lave pointere til variabler mindre end 4 bytes.

Typede og untypede pointere

Pointere kommer i to varianter: typede og untypede. En typed pointer er en pointer til en bestemt type, f.eks. en pointer til en TPoint. En untyped pointer er "bare en pointer", altså indeholder den ingen information om hvilken type den peger på.

Pointer-operationer

Når man arbejder med pointers er der nogen forskellige symboler og funktioner man kan få brug for. De vigtigste er:
  • ^ - dette symbol kan have to forskellige meninger. Læs afsnittet nedenfor for en forklaring.
  • @ - konstruerer en pointer.
  • nil - betyder nul, og er an værdi man kan tildele en pointer for at få den til atb pege på ingen ting. Man kan også checke en pointer for nil for at se om den peger på noget, men til det er Assigned() bedre.
  • Assigned() - er der noget "i den anden ende" af min pointer, dvs. peger den rent faktisk på noget?
  • New() - allokerer hukommelse for den type en typed pointer peger på og sætter pointeren til at bepe på den nyligt allokerede pointer.
  • Dispose() - deallokerer hukommelse allokeret med New() og sætter pointeren til nil.

"Hat"-symbolet "^"

Som sagt kan dette symbol betyde to forskellige ting. Man kan enten bruge det til at deklarare en variabel eller type som en pointer til en anden type. Det gør man ved at sætte hat-tegnet foran typen.
Eksempel:
var
  Punkt: TPoint;
  PPunkt: ^TPoint;
Nu er variablen Punkt en normal TPoint record, og variablen PPunkt (pointer til punkt) er så en pointer til en TPoint. Den anden deklaration kan dog også skrives på en anden måde, nemlig som:
var
  PPunkt: PPoint;
Og her er PPoint så en type deklareret sammen med TPoint typen på denne måde:
type
  PPoint: ^TPoint;
  TPoint = record
    x: integer;
    Y: integer;
  end;
Altså kan man både laven en pointer type som en "engangstype" ved at bruge hat-tegnet i sin var-sektion, eller man kan direkte deklarere en type der er en pointer.
Den anden måde hat-symbolet kan bruges på er til at dereferere en pointer, altså til at få fat på indholdet af det pointeren peger på. Det kan kun lade sig gøre at dereferere en typed pointer, fordi det ikke giver nogen mening at dereferere en untyped pointer. Her er et eksempel hver jeg bruger hat-tegnet på begge måder:
var
  p: ^TPoint;
begin
  // Se lidt længere nede i artiklen om New() og Dispose()
  New(p);
  p^.x := 3;
  p^.y := 5;
  ShowMessage('Punktet er: (IntToStr(p^.x) + ', ' + IntToStr(p^.y) + ')');
  Dispose(p);
end;

At-tegnet "@"

@ (som jo hedder "at" på engelsk) bruges til at lave en pointer. Man kan bruge den hvis man f.eks. har et API kald som skal have en pointer til en variabel. Når man bruger @ skal man huske at den som standard laver untypede pointere, så når man skal bruge den pointer man har lavet skal man først typecaste den til en bestemt pointer-type. Her er et eksempel der viser hvordan man bruger @ og som også viser forskellen på typede og untypede pointere:
var
  Punkt: TPoint; // ikke en pointer
  Pntr: Pointer; // untyped pointer
  PPunkt: PPoint; // typed pointer
begin
  Punkt := Point(3, 5);
  Pntr := @Punkt; // nu er Pntr en pointer til Punkt
  (*
  // Her er noget kode som ikke vil kompilere:
  Pntr^.x := 5;
  // Grunden er at kompileren ikke har nogen chance for at vide at
  // Pntr peger på en TPoint, og derfor ved den ikke at der er en
  // x-værdi i det Pntr peger på.
  *)
  // Nu typecaster jeg den untypede Pntr pointer til en PPoint
  // typed pointer, så kan jeg bruge den ti noget
  PPunkt := PPoint(Pntr);
  ShowMessage('X-værdi: ' + IntToStr(PPunkt^.x));
end;

Assigned()

Assigned() funktionen checker om en pointer er gyldig. Grunden til at Assigned() funktionen er lavet er at hvis man forsøger at dereferere en ugyldig pointer så får man en access violation. Nogen eksempler på steder man kan få brug for Assiged() er: Returværdier fra funktioner, parametre sendt til en procedure/funktion, og properties i objekter.

New() og Dispose()

New() og Dispose() funktionerne bruger man hvis man deklarerer en variabel af en pointer-type. Når man deklarerer en variabel der ikke er af en pointer-type bliver der normalt automatisk allokeret hukommelse til variablen, men hvis man deklarerer en pointer-type bliver der ikke automatisk allokeret hukommelse. Det er det man bruger New() til. Dispose() deallokerer hukommelse allokeret med New().
De to procedurer er meget simple at bruge, men de kan godt virke lidt tricky i starten. For det første er de procedurer, så de giver ikke nogen retur-værdi som man ellers kunne forvente, og for det andet så skal man faktisk give den pointer man vil have (de)allokeret hukommelse til som parameter til procedurerne.
Jeg har allerede lavet et eksempel med de to procedurer nederst i afsnitte om hat-tegnet.

Pointere og objekter

Til sidst vil jeg sige lidt om pointere og objekt-typer. Alle objekter (klasser der nedarver fra TObject) er faktisk pointere selvom man ikke opdager det når man arbejder med dem normalt. Det vil sige at man kan bruge Assigned() og nil til at checke om en objekt-variabel indeholder et objekt. Man kan også se det på at man sagtens kan sende et objekt som en parameter til en procedure der ellers forventer en untypet pointer, f.eks. Add() metoden i TList objektet.

Til sidst

Nu er der faktisk ikke andet at sige end at jeg håber du har fået noget ud af at læse artiklen! Hvis der er noget du er i tvivl om, så bare spørg i forummet eller send mig en email!


Hvad synes du om denne artikel? Giv din mening til kende ved at stemme via pilene til venstre og/eller lægge en kommentar herunder.

Del også gerne artiklen med dine Facebook venner:  

Kommentarer (2)

User
Bruger #1680 @ 22.10.03 21:09
det er alt sammen meget godt, men HVAD skal man dog bruge en pointer til?! jeg kan altså ikke se formålet med en såkaldt pointer
User
Bruger #1474 @ 28.11.03 11:29
Hvis du ikke bruger pointers, kopiere du data i din ram, i stedet for at referere. Det vil betyde at du har det samme data liggende flere steder i din hukommelse - det er jo i sig selv ret dumt! Specielt kan det mærkes, hvis du arbejder med meget data, foreksempel billeder osv. Med pointers kan du refere til data, istedet for at kopiere. Det i sig selv giver mange fordele. Hvis du eksempelvis vil manipulere dine data er det meget uoverskuligt, hvis du har de samme data liggende forskellige steder, hvor imod med pointers vil havde meget mere overskuelighed. Der er mange mange mange fordele ved at bruge pointers!
Du skal være logget ind for at skrive en kommentar.
t