Pointeurs et types pointeur (Object Pascal)

De Appmethod Topics
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. Les pointeurs occupent quatre octets en mémoire.

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 Object Pascal.

Présentation des pointeurs

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


 1 var
 2           X, Y: Integer;   // X et Y sont des variables Integer
 3           P: ^Integer     // P pointe sur un Integer
 4 begin
 5             X := 17;      // affecte une valeur à X
 6             P := @X;      // affecte l'adresse de X à P
 7             Y := P^;      // déréférence P; affecte le résultat à 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 affecte une valeur à X et la ligne 6 affecte 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'affecte à 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 @ dans Expressions (Object Pascal) et Types procéduraux.

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 pointeur, c'est-à-dire qu'il renvoie la valeur stockée à l'adresse mémoire contenue dans le pointeur.

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'affectation. Mais les pointeurs sont utiles pour plusieurs raisons. Tout d'abord, comprendre les pointeurs permet de mieux comprendre le langage Object Pascal, 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 Object Pascal 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 affecte 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 affectation copie simplement des données binaires brutes de R vers I sans les convertir.

Outre l'affectation du résultat d'une opération @, plusieurs routines standard permettent d'affecter une valeur à un pointeur. Les procédures New et GetMem affectent 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 affectée à tout pointeur. Quand la valeur nil est affecté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 (Object Pascal).

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 (Object Pascal).

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+} ou {$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