Pointeurs et types pointeur (Delphi)
Sommaire
- 1 Présentation des pointeurs
- 1.1 Utilisation de la syntaxe étendue avec les pointeur
- 2 Types pointeur
- 2.1 Pointeurs de caractère
- 2.2 Pointeur de type octet
- 2.3 Pointeurs avec contrôle du type
- 2.4 Autres types standard de pointeurs
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. |
Aucun commentaire:
Enregistrer un commentaire