Types simples (Delphi)
Sommaire
Remonter à Types de données, variables et constantes - Index
Les types simples, qui comportent les types ordinaux et les types réels, définissent des ensembles ordonnés de valeurs.
Types ordinaux
Les types ordinaux incluent les types entier, caractère, booléen, énuméré et intervalle. Un type ordinal définit un ensemble ordonné de valeurs dans lequel chaque valeur, sauf la première, a un prédécesseur unique et chaque valeur, sauf la dernière, a un successeur unique. En outre, chaque valeur a un rang qui détermine l'ordre du type. Dans la plupart des cas, si une valeur a le rang n, son prédécesseur a le rang n-1 et son successeur a le rang n+1.
Pour les types entiers, le rang d'une valeur est la valeur elle-même. Les types sous-intervalle conservent les rangs de leurs types de base. Pour les autres types ordinaux, la première valeur a par défaut le rang 0, la suivante a le rang 1, et ainsi de suite. La déclaration d'un type énuméré peut explicitement redéfinir cette valeur par défaut.
Plusieurs fonctions prédéfinies opèrent sur les valeurs ordinales et les identificateurs de type. Les plus importantes sont résumées ci-dessous.
Fonction | Paramètre | Valeur de retour | Remarques |
---|---|---|---|
Expression ordinale |
Rang de la valeur de l'expression |
Ne prend pas d'arguments Int64. | |
Expression ordinale |
Prédécesseur de la valeur de l'expression |
||
Succ |
Expression ordinale |
Successeur de la valeur de l'expression |
|
High |
Identificateur de type ordinal ou variable de type ordinal |
Plus grande valeur du type |
Opère également sur les types chaîne courte et les tableaux. |
Identificateur de type ordinal ou variable de type ordinal |
Plus petite valeur du type |
Opère également sur les types chaîne courte et les tableaux. |
Par exemple, High(Byte) renvoie 255 car la plus grande valeur du type Byte est 255, et Succ(2) renvoie 3 car 3 est le successeur de 2.
Les procédures standard Inc et Dec incrémentent et décrémentent la valeur d'une variable ordinale. Par exemple, Inc(I) est équivalent à I := Succ(I)
et, si I
est une variable entière, c'est également équivalent à I := I + 1
.
Types entiers
Un type entier représente un sous-ensemble des nombres intégraux.
Les types entiers peuvent être dépendants de la plate-forme et indépendants de la plate-forme.
Types entiers dépendants de la plate-forme
Les types entiers dépendants de la plate-forme sont transformés pour s'ajuster à la taille en bits de la plate-forme du compilateur en cours. Les types entiers dépendants de la plate-forme sont NativeInt, NativeUInt, LongInt et LongWord. L'utilisation de ces types est souhaitable dans la mesure du possible, puisqu'ils donnent de meilleures performances pour le système d'exploitation et la CPU sous-jacente. Le tableau suivant illustre leurs intervalles et les formats de stockage pour le compilateur Delphi.
Types entiers dépendants de la plate-forme
Type | Intervalle | Format | Alias |
---|---|---|---|
|
Signé sur 32 bits sur les plates-formes 32 bits ou |
||
|
Non signé sur 32 bits sur les plates-formes 32 bits ou |
||
|
Plates-formes Windows 32 bits et 64 bits |
||
|
Plates-formes Windows 32 bits et 64 bits |
- Remarque : Les plates-formes 32 bits incluent Windows 32 bits, OSX32, iOS 32 bits et Android.
Types entiers indépendants de la plate-forme
Les types entiers indépendants de la plate-forme ont toujours la même taille, quelle que soit la plate-forme utilisée. Les types entiers indépendants de la plate-forme incluent ShortInt, SmallInt, LongInt, Integer, Int64, Byte, Word, LongWord, Cardinal et UInt64.
Types entiers indépendants de la plate-forme
Type | Intervalle | Format | Alias |
---|---|---|---|
|
Signé sur 8 bits |
||
|
Signé sur 16 bits |
||
|
Signé sur 32 bits |
||
|
Signé sur 32 bits |
||
|
Signé sur 64 bits |
||
Byte |
|
Non signé sur 8 bits |
|
|
Non signé sur 16 bits |
||
|
Non signé sur 32 bits |
||
|
Non signé sur 32 bits |
||
|
Non signé sur 64 bits |
En général, les opérations arithmétiques sur les entiers renvoient une valeur de type Integer, qui est équivalente au type LongInt sur 32 bits. Les opérations ne renvoient une valeur de type Int64 que si elles portent sur un ou plusieurs opérandes Int64. Par conséquent, le code suivant produit des résultats incorrects :
var I: Integer; J: Int64; ... I := High(Integer); J := I + 1;
Pour obtenir une valeur de retour Int64 dans cette situation, transtypez I
en Int64 :
... J := Int64(I) + 1;
Pour de plus amples informations, voir Opérateurs arithmétiques.
Remarque : Certaines routines standard qui attendent des arguments entiers tronquent les valeurs Int64 à 32 bits. Toutefois, les routines High, Low, Succ, Pred, Inc, Dec, IntToStr et IntToHex supportent complètement les arguments Int64. De même, les fonctions Round, Trunc, StrToInt64 et StrToInt64Def renvoient des valeurs Int64. Quelques routines n'acceptent pas les valeurs Int64.
Quand vous incrémentez la dernière valeur ou décrémentez la première valeur d'un type entier, le résultat boucle sur le début ou la fin de l'intervalle. Par exemple, le type ShortInt a l'étendue -128..127 ; donc après l'exécution du code suivant :
var I: Shortint; ... I := High(Shortint); I := I + 1;
la valeur de I
est -128. Si la vérification des limites de compilation est activée, ce code génère néanmoins une erreur d'exécution.
Types caractères
Les types caractères sont Char, AnsiChar, WideChar, UCS2Char et UCS4Char :
- Char dans l'implémentation en cours est équivalent à WideChar, puisque le type de chaîne par défaut est maintenant UnicodeString. Puisque l'implémentation de Char peut changer dans les futures releases, il est judicieux d'utiliser la fonction standard SizeOf plutôt qu'une constante codée en dur lors de l'écriture de programmes qui doivent gérer des caractères de tailles différentes.
- Les valeurs AnsiChar sont des caractères sur un octet (8 bits) ordonnés selon le jeu de caractères local éventuellement multi-octets.
- Les caractères WideChar utilisent plus d'un octet pour représenter chaque caractère. Dans les implémentations en cours, les valeurs WideChar sont des caractères sur un mot (16 bits) ordonnés selon le jeu de caractères Unicode (ils pourraient être plus longs dans de futures implémentations). Les 256 premiers caractères Unicode correspondent aux caractères ANSI.
- UCS2Char est un alias pour WideChar.
- UCS4Char est utilisé pour fonctionner avec des caractères Unicode sur 4 octets.
Une constante chaîne de longueur 1, comme 'A', peut désigner une valeur caractère. La fonction prédéfinie Chr renvoie la valeur caractère pour tout entier de l'étendue de WideChar ; par exemple Chr(65) renvoie la lettre A.
Les valeurs AnsiChar et WideChar, comme les entiers, bouclent quand elles sont décrémentées ou incrémentées au-delà du début ou de la fin de leur étendue (à moins que la vérification des limites ne soit activée). Ainsi, une fois le code suivant exécuté :
var Letter: AnsiChar; I: Integer; begin Letter := High(Letter); for I := 1 to 66 do Inc(Letter); end;
Letter
a la valeur A (ASCII 65).
Remarque : Le type AnsiChar n'est pas pris en charge par les compilateurs mobiles Delphi, mais il est utilisé par les compilateurs de bureau Delphi. Pour davantage d'informations, voir Migration du code Delphi en mobile depuis le bureau.
Types booléens
Les 4 types booléens prédéfinis sont Boolean, ByteBool, WordBool et LongBool. Boolean est le type de prédilection. Les autres types existent pour fournir la compatibilité avec les bibliothèques des autres langages et systèmes d'exploitation.
Une variable Boolean occupe un octet de mémoire, une variable ByteBool occupe également un octet, une variable WordBool occupe 2 octets (un mot), et une variable LongBool occupe 4 octets (2 mots).
Les valeurs booléennes sont désignées par les constantes prédéfinies True et False. Les relations suivantes s'appliquent :
Boolean | ByteBool, WordBool, LongBool |
---|---|
|
|
|
|
|
|
|
|
|
|
Une valeur de type ByteBool, LongBool ou WordBool est considérée comme True quand son rang est non nul. Si une telle valeur apparaît dans un contexte où un Boolean est attendu, le compilateur convertit automatiquement toute valeur de rang non nul en True.
Les remarques précédentes portent sur le rang des valeurs booléennes, non pas sur les valeurs mêmes. Dans Delphi, les expressions booléennes ne peuvent être comparées avec des entiers ou des réels. Par exemple, si X est une variable entière, l'instruction :
if X then ...;
génère une erreur de compilation. Le transtypage de la variable en un type booléen n'est pas fiable, mais les deux alternatives suivantes fonctionnent.
if X <> 0 then ...; { use an expression that returns a Boolean value } ... var OK: Boolean; { use a Boolean variable } ... if X <> 0 then OK := True; if OK then ...;
Types énumérés
Un type énuméré définit un ensemble ordonné de valeurs en énumérant simplement les identificateurs désignant ces valeurs. Les valeurs n'ont pas de signification propre. Pour déclarer un type énuméré, utilisez la syntaxe suivante :
type typeName = (val1, ...,valn)
où typeName
et chaque val
sont des identificateurs valides. Par exemple, la déclaration :
type Suit = (Club, Diamond, Heart, Spade);
définit une suite énumérée appelée Suit
dont les valeurs possibles sont Club
, Diamond
, Heart
et Spade
, où Ord(Club) renvoie 0, Ord(Diamond) renvoie 1, et ainsi de suite.
Quand vous déclarez un type énuméré, vous déclarez chaque val
comme une constante de type typeName
. Si les identificateurs val
sont utilisés dans un autre but dans la même portée, il y a un conflit de nom. Si, par exemple vous déclarez le type :
type TSound = (Click, Clack, Clock)
Malheureusement, Click est aussi le nom d'une méthode définie pour TControl et tous les objets de la VCL qui en descendent. Donc, si vous écrivez une application et créez un gestionnaire d'événement tel que :
procedure TForm1.DBGridEnter(Sender: TObject); var Thing: TSound; begin ... Thing := Click; end;
vous obtiendrez une erreur de compilation ; le compilateur interprète Click dans la portée de la procédure comme une référence à la méthode Click d'un TForm. Vous pouvez contourner ce problème en qualifiant l'identificateur ; si TSound est déclarée dans MyUnit, vous devez utiliser :
Thing := MyUnit.Click;
Une meilleure solution consiste néanmoins à choisir des noms de constantes qui ne rentrent pas en conflit avec d'autres identificateurs. Exemples :
type TSound = (tsClick, tsClack, tsClock); TMyColor = (mcRed, mcBlue, mcGreen, mcYellow, mcOrange); Answer = (ansYes, ansNo, ansMaybe)
Vous pouvez utiliser directement la construction (val1, ..., valn)
dans les déclarations des variables, comme nom de type :
var MyCard: (Club, Diamond, Heart, Spade);
Mais si vous déclarez MyCard
de cette manière, vous ne pouvez pas déclarer une autre variable dans la même portée en utilisant ces identificateurs de constantes. Ainsi :
var Card1: (Club, Diamond, Heart, Spade); var Card2: (Club, Diamond, Heart, Spade);
génère une erreur de compilation. Mais :
var Card1, Card2: (Club, Diamond, Heart, Spade);
se compile sans problème, tout comme :
type Suit = (Club, Diamond, Heart, Spade); var Card1: Suit; Card2: Suit;
Types énumérés avec les rangs explicitement assignés
Par défaut, le rang des valeurs énumérées commence à 0 et suit la séquence dans laquelle leurs identificateurs sont listés dans la déclaration de type. Vous pouvez redéfinir cela en assignant explicitement les rangs de certaines ou de toutes les valeurs de la déclaration. Pour assigner un rang à une valeur, faites suivre son identificateur de = constantExpression, où constantExpression est une expression constante dont le résultat est un entier. Par exemple :
type Size = (Small = 5, Medium = 10, Large = Small + Medium);
définit un type appelé Size
dont les valeurs possibles sont Small
, Medium
et Large
, où Ord(Small) renvoie 5, Ord(Medium) renvoie 10 et Ord(Large) renvoie 15.
Un type énuméré est en effet un intervalle dont les valeurs inférieure et supérieure correspondent aux rangs inférieur et supérieur des constantes de la déclaration. Dans l'exemple précédent, le type Size
a 11 valeurs possibles dont le rang va de 5 à 15. (Ici, le type array[Size] of Char
représente un tableau de 11 caractères.) Seules trois de ces valeurs ont un nom, mais les autres sont accessibles par le biais de transtypages et de routines comme Pred, Succ, Inc et Dec. Dans l'exemple suivant, des valeurs "anonymes" dans l'étendue de Size
sont assignées à la variable X
.
var X: Size; X := Small; // Ord(X) = 5 X := Size(6); // Ord(X) = 6 Inc(X); // Ord(X) = 7
Toute valeur à laquelle un rang n'a pas été explicitement assigné a pour rang un de plus que celui de la valeur précédente de la liste. Si aucun rang n'est assigné à la première valeur, son rang est 0. Ici, étant donné la déclaration :
type SomeEnum = (e1, e2, e3 = 1);
SomeEnum
a seulement deux valeurs possibles : Ord(e1) renvoie 0, Ord(e2) renvoie 1, et Ord(e3) renvoie également 1 ; comme e2
et e3
ont le même rang, ils représentent la même valeur.
Les constantes énumérées sans valeur spécifique ont des informations RTTI :
type SomeEnum = (e1, e2, e3);
alors que les constantes énumérées avec une valeur spécifique (comme dans l'exemple ci-dessous) n'en ont pas :
type SomeEnum = (e1 = 1, e2 = 2, e3 = 3);
Enumérations de portée
Vous pouvez utiliser les énumérations de portée dans le code Delphi si vous activez la directive {$SCOPEDENUMS ON} du compilateur.
La directive {$SCOPEDENUMS ON ou OFF} du compilateur active ou désactive l'utilisation des énumérations de portée dans le code Delphi. {$SCOPEDENUMS ON} définit que les énumérations sont des énumérations de portée. {$SCOPEDENUMS ON} affecte des déclarations de types énumération jusqu'à la directive {$SCOPEDENUMS OFF} la plus proche. Les identificateurs de l'énumération introduite dans les types énumération déclarés après la directive {$SCOPEDENUMS ON} ne sont pas ajoutés à la portée globale. Pour utiliser un identificateur d'énumération de portée, vous devez le qualifier avec le nom du type énumération introduisant cet identificateur.
Par exemple, définissons l'unité suivante dans le fichier Unit1.pas
unit Unit1;
interface
// {$SCOPEDENUMS ON} // clear comment from this directive
type
TMyEnum = (First, Second, Third);
implementation
end.
et le programme suivant en utilisant cette unité
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils, Unit1 in 'Unit1.pas';
var
// First: Integer; // clear comment from this variable
Value: TMyEnum;
begin
try
Value := First;
// Value := TMyEnum.First;
// Value := unit1.First;
except
on E:Exception do
Writeln(E.Classname, ': ', E.Message);
end;
end.
Nous pouvons maintenant étudier les effets de la directive {$SCOPEDENUMS} du compilateur sur les portées dans lesquelles les identificateurs First
, Second
et Third
, définis dans l'énumération TMyEnum
, sont visibles.
Commencez par exécuter (F9) ce code. Le code s'exécute correctement. Cela signifie que l'identificateur First
, utilisé dans la variable
Value := First;
est l'identificateur de porté globale introduit dans le type énumération
TMyEnum = (First, Second, Third);
A présent, effacez le commentaire de la directive
{$SCOPEDENUMS ON}
du compilateur dans l'unité unit1
. Cette directive applique l'énumération TMyEnum
à générer. Lancez la commande Exécuter. L'erreur E2003 Identificateur non déclaré : 'First' est générée sur la ligne
Value := First;
Elle vous informe que la directive {$SCOPEDENUMS ON}
du compilateur empêche l'ajout de l'identificateur First
, introduit dans l'énumération TMyEnum
de portée, à la portée globale.
Pour utiliser les identificateurs introduits dans les énumérations de portée, préfixez une référence à un élément d'énumération avec son nom de type. Par exemple, effacez le commentaire dans la seconde version
Value := TMyEnum.First;
de la variable Value
(et commentez la première version de Value
). Lancez la commande Exécuter. Le programme s'exécute correctement. Cela signifie que l'identificateur First
est connu dans la portée TMyEnum
.
Maintenant, commentez la directive
// {$SCOPEDENUMS ON}
du compilateur dans unit1
. Puis effacez le commentaire de la déclaration de la variable First
First: Integer;
et utilisez à nouveau la variable
Value := First;
A présent, le code dans program Project1
ressemble à :
var
First: Integer;
Value: TMyEnum;
begin
try
Value := First;
Lancez la commande Exécuter. La ligne
First: Integer;
provoque l'erreur E2010 Types incompatibles - 'TMyEnum' et 'Integer'. Cela signifie que le conflit de nommage se produit entre l'identificateur First
de portée globale introduit dans l'énumération TMyEnum
et la variable First
. Vous pouvez contourner ce conflit en qualifiant l'identificateur First
avec l'unité unit1
dans laquelle il est défini. Pour cela, commentez à nouveau la première version de la variable Value
et effacez le commentaire de la troisième :
Value := unit1.First;
Lancez la commande Exécuter. Le programme s'exécute correctement. Cela signifie qu'à présent l'identificateur First
peut être qualifié avec la portée d'unité unit1
. Voyons ce qui se passe si nous activons à nouveau la directive
{$SCOPEDENUMS ON}
du compilateur dans unit1
. L'erreur E2003 Identificateur non déclaré : 'First' est générée sur la ligne
Value := unit1.First;
Cela signifie que {$SCOPEDENUMS ON} empêche l'ajout de l'identificateur First
de l'énumération dans la portée unit1
. A présent l'identificateur First
est ajouté seulement dans la portée TMyEnum
de l'énumération. Pour vérifier cela, utilisons à nouveau la version
Value := TMyEnum.First;
de la variable Value
. Lancez la commande Exécuter. Le code s'exécute correctement.
Types sous-intervalle
Un type sous-intervalle représente un sous-ensemble de valeurs d'un autre type ordinal (appelé le type de base). Toute construction de la forme Low..High
, où Low
et High
sont des expressions constantes du même type ordinal, Low
étant inférieur à High
, identifie un type sous-intervalle qui inclut toutes les valeurs comprises entre Low
et High
. Si par exemple, vous déclarez le type énuméré :
type TColors = (Red, Blue, Green, Yellow, Orange, Purple, White, Black);
vous pouvez définir le type sous-intervalle suivant :
type TMyColors = Green..White;
Ici TMyColors inclut les valeurs Green
, Yellow
, Orange
, Purple
et White
.
Vous pouvez utiliser des constantes numériques ou des caractères (constantes chaîne de longueur 1) pour définir des types sous-intervalle :
type SomeNumbers = -128..127; Caps = 'A'..'Z';
Quand vous utilisez des constantes numérique ou caractère pour définir un sous-intervalle, le type de base est le plus petit type entier ou caractère contenant l'intervalle spécifié.
La construction LowerBound..UpperBound
fonctionne directement comme nom de type, vous pouvez donc l'utiliser directement dans des déclarations de variables. Par exemple :
var SomeNum: 1..500;
déclare une variable entière dont la valeur est dans l'intervalle allant de 1 à 500.
Le rang de chaque valeur d'un sous-intervalle est préservé depuis le type de base. (Dans le premier exemple, si Color
est une variable contenant la valeur Green
, Ord(Color) renvoie 2, que Color
soit de type TColors ou TMyColors.) Les valeurs ne bouclent pas au début ou à la fin du sous-intervalle même si le type de base est de type entier ou caractère ; l'incrémentation ou la décrémention au-delà des limites d'un sous-intervalle convertit simplement la valeur dans le type de base. Ainsi, tandis que :
type Percentile = 0..99; var I: Percentile; ... I := 100;
produit une erreur, le code suivant :
... I := 99; Inc(I);
assigne la valeur 100 à I
(à moins que la vérification des limites de compilation ne soit activée).
L'utilisation d'expressions constantes dans les définitions des sous-intervalles introduit une difficulté syntaxique. Dans toute déclaration de type, quand le premier caractère significatif après = est une parenthèse gauche, le compilateur suppose qu'un type énuméré est défini. Ainsi, le code :
const X = 50; Y = 10; type Scale = (X - Y) * 2..(X + Y) * 2;
produit une erreur. Pour contourner ce problème, il faut réécrire la déclaration de type afin d'éviter la parenthèse de début :
type Scale = 2 * (X - Y)..(X + Y) * 2;
Types réels
Un type réel définit un ensemble de nombres pouvant être représentés avec la notation à virgule flottante. Le tableau ci-dessous donne les intervalles et les formats de stockage des types réels sur les plates-formes 32 bits et 64 bits.
Types réels
Type | Intervalle positif approximatif | Chiffres significatifs | Taille en octets |
---|---|---|---|
Real48 | 2.9e-39 .. 1.7e+38
|
11-12 | 6 |
Single | 1.5e-45 .. 3.4e+38
|
7-8 | 4 |
Double | 5.0e-324 .. 1.7e+308
|
15-16 | 8 |
Real | 5.0e-324 .. 1.7e+308
|
15-16 | 8 |
Extended |
|
10-20 15-16 |
10 8 |
Comp | -9223372036854775808.. 9223372036854775807 (-263.. 263-1)
|
10-20 | 8 |
Currency | -922337203685477.5808.. 922337203685477.5807 (-(263+1)/10000.. 263/10000)
|
10-20 | 8 |
Les remarques suivantes s'appliquent aux types réels :
- Real48 est conservé pour la compatibilité descendante. Comme son format de stockage n'est pas natif à l'architecture des processeurs Intel, ce type produit des performances plus mauvaises que les autres types à virgule flottante.
- Le type Real48 sur 6 octets s'appelait Real dans les versions précédentes de Object Pascal. Si vous recompilez du code utilisant l'ancien type Real sur 6 octets dans Delphi, vous pouvez le changer en Real48. Vous pouvez également utiliser la directive
{$REALCOMPATIBILITY ON}
du compilateur qui revient à l'interprétation de Real comme un type sur 6 octets.
- Extended offre une plus grande précision sur les plates-formes 32 bits que les autres types réels.
- Sur les plates-formes 64 bits, Extended est l'alias d'un Double ; c'est-à-dire que la taille du type de données Extended est de 8 octets. Vous avez ainsi moins de précision en utilisant un type Extended sur les plates-formes 64 bits que sur les plates-formes 32 bits, où la taille du type Extended est de 10 octets. Par conséquent, si vos applications utilisent le type de données Extended et que vous comptez sur la précision pour les opérations à virgule flottante, cette différence de taille peut affecter vos données. Soyez prudent en utilisant Extended si vous créez des fichiers de données qui doivent être partagés sur plusieurs plates-formes. Pour de plus amples informations, voir Le type de données Extended est de 2 octets plus petit sur les systèmes Windows 64 bits.
- Le type Comp est un type natif de l'architecture des processeurs Intel et représente un entier sur 64 bits. Il est néanmoins classé parmi les réels car il ne se comporte pas comme un type ordinal. (Par exemple, il n'est pas possible d'incrémenter ou de décrémenter une valeur Comp.) Comp est conservé uniquement pour la compatibilité descendante. Utilisez le type Int64 pour de meilleures performances.
- Currency est un type de données à virgule fixe qui minimise les erreurs d'arrondi dans les calculs monétaires. Il est stocké dans un entier sur 64 bits, les 4 chiffres les moins significatifs représentant implicitement les positions décimales. Lorsqu'il est combiné à d'autres types réels dans des affectations et des expressions, les valeurs Currency sont automatiquement divisées ou multipliées par 10000.
Voir aussi
- Types de données Delphi
- A propos des types de données (Delphi)
- Types chaîne (Delphi)
- Types structurés (Delphi)
- Pointeurs et types pointeur (Delphi)
- Types procéduraux (Delphi)
- Types variants (Delphi)
- Compatibilité et identité de types (Delphi)
- Types de données, variables et constantes - Index (Delphi)
- Variables (Delphi)
- Constantes déclarées
- Formats de données internes (Delphi)
- Types de données Windows 64 bits comparés aux types de données Windows 32 bits
- Problèmes d'arrondi des nombres à virgule flottante
- Routines de comparaison des nombres à virgule flottante
- Routines d'arrondi des nombres à virgule flottante
- Routines de contrôle des nombres à virgule flottante
- Routines intrinsèques de Delphi