Pointeurs et types pointeur (Delphi))

De RAD Studio
Aller à : navigation, rechercher

Remonter à Types de données, variables et constantes - Index


Un pointeur est une variable qui désigne une adresse mémoire. Quand un pointeur contient l'adresse d'une autre variable, on dit qu'il pointe sur l'emplacement en mémoire de cette variable ou sur les données qui y sont stockées. Dans le cas d'un tableau ou d'un type structuré, un pointeur contient l'adresse du premier élément de la structure. Si cette adresse est déjà prise, le pointeur contient l'adresse vers le premier élément.

Les pointeurs sont typés afin d'indiquer le type de données stockées à l'adresse qu'ils contiennent. Le type général Pointer peut représenter un pointeur sur tous les types de données alors que d'autres pointeurs, plus spécialisés, ne pointent que sur des types de données spécifiques. Le type PByte est utilisé pour toute donnée octet qui n'est pas une donnée caractère.

Sur les plates-formes 32 bits, un pointeur occupe quatre octets en mémoire sous la forme d'une adresse 32 bits. Sur les plates-formes 64 bits, un pointeur occupe huit octets en mémoire sous la forme d'une adresse 64 bits.

Cette rubrique contient des informations sur les points suivants :

  • Présentation générale des types pointeur
  • Déclaration et utilisation des types pointeur pris en charge par Delphi.


Présentation des pointeurs

Pour comprendre comment les pointeurs fonctionnent, examinez l'exemple suivant :

 1         var
 2           X, Y: Integer;  // X and Y are Integer variables
 3           P: ^Integer;     // P points to an Integer
 4         begin
 5           X := 17;      // assign a value to X
 6           P := @X;      // assign the address of X to P
 7           Y := P^;      // dereference P; assign the result to Y
 8         end;

La ligne 2 déclare X et Y comme variables de type Integer. La ligne 3 déclare P comme un pointeur sur une valeur Integer ; cela signifie que P peut pointer sur l'adresse de X ou de Y. La ligne 5 assigne une valeur à X et la ligne 6 assigne l'adresse de X (désignée par @X) à P. Enfin, la ligne 7 récupère la valeur située à l'emplacement pointé par P (désigné par ^P) et l'assigne à Y. Après l'exécution de ce code, X et Y ont la même valeur, à savoir 17.

L'opérateur @ utilisé ici pour obtenir l'adresse d'une variable agit également sur les fonctions et les procédures. Pour plus d'informations, voir L'opérateur @ et Types procéduraux dans les instructions et les expressions.

Le symbole ^ a deux fonctions, toutes deux illustrées dans notre exemple. Quand il apparaît avant un identificateur de type :

^typeName

le symbole ^ désigne un type qui représente des pointeurs sur des variables de type typeName.

Quand il apparaît après une variable pointeur :

pointer^

le symbole ^ déréférence le pointer, c'est-à-dire qu'il renvoie la valeur stockée à l'adresse mémoire contenue dans le pointer.

Cet exemple peut sembler un moyen bien compliqué pour copier la valeur d'une variable dans une autre, puisque cela peut s'effectuer par une simple instruction d'assignation. Mais les pointeurs sont utiles pour plusieurs raisons. Tout d'abord, comprendre les pointeurs permet de mieux comprendre le langage Delphi, car fréquemment des pointeurs agissent en sous-main dans le code, même quand ils n'apparaissent pas explicitement. Tout type de données nécessitant des blocs de mémoire importants alloués dynamiquement utilise des pointeurs. Ainsi, les variables chaîne longue sont implicitement des pointeurs, tous comme les variables d'instance de classe. De plus, certaines techniques de programmation avancée nécessitent l'utilisation de pointeurs.

Enfin, les pointeurs sont parfois le seul moyen de contourner les exigences de Delphi sur le typage des données. En faisant référence à une variable à l'aide d'un Pointer générique, en transtypant ce Pointer dans un type plus spécifique, puis en le déréférençant, vous pouvez traiter les données stockées dans n'importe quelle variable comme appartenant à un type quelconque. Par exemple, le code suivant assigne les données stockées dans une variable réelle à une variable entière.

type
  PInteger = ^Integer;
var
  R: Single;
  I: Integer;
  P: Pointer;
  PI: PInteger;
begin
  ...
  P := @R;
  PI := PInteger(P);
  I := PI^;
end;

Bien évidemment, les réels et les entiers ne sont pas stockés en utilisant le même format. Cette assignation copie simplement des données binaires brutes de R vers I sans les convertir.

Outre l'assignation du résultat d'une opération @, plusieurs routines standard permettent d'assigner une valeur à un pointeur. Les procédures New et GetMem assignent une adresse mémoire à un pointeur existant, alors que les fonctions Addr et Ptr renvoient un pointeur sur l'adresse ou la variable spécifiée.

Il est possible de qualifier un pointeur déréférencé ou de l'utiliser comme qualificateur, comme dans l'expression P1^.Data^.

Le mot réservé nil est une constante spéciale qui peut être assignée à tout pointeur. Quand la valeur nil est assignée à un pointeur, le pointeur ne désigne plus rien.

Utilisation de la syntaxe étendue avec les pointeur

La directive {$EXTENDED} du compilateur affecte l'utilisation du symbole ^. Quand {$X+} est en vigueur (par défaut), vous pouvez omettre le symbole ^ lors du référencement des pointeurs. Le symbole ^ est toujours requis lors de la déclaration d'un pointeur et pour la résolution de l'ambiguïté générée quand un pointeur pointe sur un autre pointeur. Pour de plus amples informations, voir Syntaxe étendue (Delphi).

Quand la syntaxe étendue est activée, vous pouvez omettre le symbole ^ lors du référencement sur un pointeur, comme dans l'exemple suivant :

{$X+}
 type
   PMyRec = ^TMyRec;
   TMyRec = record
     Data: Integer;
   end;

 var
   MyRec: PMyRec;

 begin
   New(MyRec);
   MyRec.Data := 42;  {#1}
 end.

Quand la syntaxe étendue n'est pas activée, la ligne marquée {#1} devrait être typiquement exprimée sous la forme :

 MyRec^.Data := 42;

Types pointeur

Il est possible de déclarer tout type de pointeur en utilisant la syntaxe :

type  pointerTypeName = ^type

Lorsque vous définissez un enregistrement ou d'autres types de données, il peut être utile de définir également un pointeur sur ce type. Cela simplifie la manipulation des instances de ce type sans avoir à copier de gros blocs de mémoire.

Remarque : Vous pouvez déclarer un type pointeur avant de déclarer le type sur lequel il pointe.

Les types standard de pointeur ont de nombreuses fonctions. Le type le plus versatile Pointer peut pointer sur les données de tout type. Mais une variable Pointer ne peut être déréférencée ; placer le symbole ^ après une variable Pointer déclenche une erreur de compilation. Pour accéder aux données référencées par une variable Pointer, il faut d'abord la transtyper dans un autre type de pointeur, puis alors seulement la déréférencer.

Pointeurs de caractère

Les types fondamentaux PAnsiChar et PWideChar représentent des pointeurs sur, respectivement, des valeurs AnsiChar et WideChar . Le type générique PChar représente un pointeur sur un Char (c'est-à-dire, dans son implémentation en cours, sur un WideChar). Ces pointeurs de caractère sont utilisés pour manipuler des chaînes à zéro terminal. Voir "Manipulation des chaînes à zéro terminal" dans Types chaîne (Delphi).)

Remarque : Ne transtypez pas les types de pointeur non caractère en PChar pour faire une arithmétique de pointeur. Utilisez à la place le type de pointeur PByte, qui est déclaré avec la directive {$POINTERMATH ON} du compilateur.

Pointeur de type octet

Le type fondamental PByte représente un pointeur sur toute donnée octet qui n'est pas une donnée caractère. Le type est déclaré avec la directive {$POINTERMATH ON} du compilateur :

function TCustomVirtualStringTree.InternalData(Node: PVirtualNode): Pointer;
begin
  if (Node = FRoot) or (Node = nil) then
    Result := nil
  else
    Result := PByte(Node) + FInternalDataOffset;
end;

Pointeurs avec contrôle du type

La directive $T contrôle les types des valeurs pointeur générées par l'opérateur @. Cette directive prend la forme suivante :

{$T+} or {$T-}

En mode {$T-}, le type résultant d'une utilisation de l'opérateur @ est toujours un pointeur non typé compatible avec tous les types de pointeurs. En mode {$T+}, lorsque @ est appliqué à une référence de variable, le type du résultat est ^T, où T est uniquement compatible avec les pointeurs du type de la variable.

Autres types standard de pointeurs

Les unités System et SysUtils déclarent plusieurs types de pointeur standard couramment utilisés.

Utilisez la directive {POINTERMATH <ON|OFF>} pour activer ou désactiver l'arithmétique de pointeur pour tous les pointeurs typés, afin que l'incrémentation/décrémentation se fasse par taille d'élément.

Sélection de types de pointeur déclarés dans les unités System et SysUtils :

Type de pointeur Pointe sur des variables de type

PString

UnicodeString

PAnsiString

AnsiString

PByteArray

TByteArray (déclaré dans SysUtils). Utilisé pour transtyper dynamiquement de la mémoire allouée pour les tableaux.

PCurrency, PDouble, PExtended, PSingle

Currency, Double, Extended, Single

PInteger

Integer

POleVariant

OleVariant

PShortString

ShortString. Utilisé pour adapter du code ancien utilisant le type PString.

PTextBuf

TTextBuf (déclaré dans SysUtils). TTextBuf est le type interne de tampon d'un enregistrement fichier TTextRec.

PVarRec

TVarRec (déclaré dans System)

'PVariant

Variant

PWideString

WideString

PWordArray

TWordArray (déclaré dans SysUtils). Utilisé pour transtyper dynamiquement de la mémoire allouée pour des tableaux de valeurs sur deux octets.

Voir aussi