Types de données simples - Delphi Pascal

Types de Données


Les types simples qui comportent les types scalaire et réel, définissent des ensembles ordonnés de valeurs.
Les types scalaires traités dans cette rubrique sont :

  • Types entiers
  • Types caractère
  • Types booléens
  • Types énumérés
  • Types réels (à virgule flottante)
Types scalaires

Les types scalaires sont les types entier, caractère, booléen, énuméré et intervalle. Un type scalaire définit un ensemble ordonné de valeurs dont chaque valeur, sauf la première, a un prédécesseur unique et dont chaque valeur, sauf la dernière, a un successeur unique. 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 entiers, le rang d'une valeur est la valeur elle-même. Les types intervalle conservent les rangs de leurs types de base. Pour les autres types scalaires, la première valeur a par défaut le rang 0, la suivante a le rang 1, etc. La déclaration d'un type énuméré doit explicitement redéfinir la valeur par défaut.
Plusieurs fonctions prédéfinies portent sur des valeurs scalaires et les identificateurs de type. Les plus importantes sont résumées ci-dessous.


FonctionParamètreValeur de retourRemarques
Ord
Expression scalaire
Rang de la valeur de l'expression
Ne prend pas d'arguments Int64.
Pred
Expression scalaire
Prédécesseur de la valeur de l'expression

Succ
Expression scalaire
Successeur de la valeur de l'expression

High
Identificateur de type scalaire ou variable de type scalaire
Plus grande valeur du type
Opère également sur les types de chaîne courte et les tableaux.
Low
Identificateur de type scalaire ou variable de type scalaire
Plus petite valeur du type
Opère également sur les types de 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 scalaire. 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 de l'ensemble des nombres. Les types entiers génériques sont Integer et Integer ; utilisez-les dans la mesure du possible car ils donnent de meilleures performances avec la CPU et le système d'exploitation utilisés. Le tableau suivant spécifie leur étendue et leur format de stockage pour le compilateur Delphi.

Types entiers génériques


TypeEtendueFormat
Integer
-2147483648..2147483647
32 bits signé
Cardinal
0..4294967295
32 bits non signé



Types entiers fondamentaux

Les types entiers fondamentaux sont : ShortInt, SmallInt, LongInt, Int64, Byte, Word, LongWord et UInt64.

Types entiers fondamentaux :



TypeEtendueFormat
ShortInt
-128..127
8 bits signé
Int8
SmallInt
-32768..32767
16 bits signé
Int16
LongInt
-2147483648..2147483647
32 bits signé
Int32
Int64
-2^63..2^63-1
64 bits signé

Byte
0..255
8 bits non signé
UInt8
Word
0..65535
16 bits non signé
UInt16
LongWord
0..4294967295
32 bits non signé
UInt32
UInt64
0..2^64-1
64 bits non signé


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 exemple, le code suivant donne des résultats incorrects :

var I: Integer; J: Int64; ... I := High(Integer); J := I + 1;

Pour obtenir une valeur renvoyée de type Int64, convertissez I en UInt64 :

 ... J := Int64(I) + 1;

Remarque : Certaines routines standard qui attendent des arguments entiers tronquent les valeurs Int64 à 32 bits. Néanmoins, les routines High, Low, Succ, Pred, Inc, Dec, IntToStr et IntToHex supportent entièrement 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.

Si vous incrémentez la dernière valeur ou si vous décrémentez la première valeur d'un type entier, le résultat boucle sur le début ou la fin de l'étendue. 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;

I a la valeur -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 de caractère fondamentaux sont AnsiChar et WideChar. Les valeurs AnsiChar sont des caractères sur un octet (8 bits) ordonnés selon le jeu de caractères local qui est peut-être multi-octets.
Les caractères WideChar utilisent plus d'un octet pour représenter chaque caractère. 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.
Le type de caractère général est Char, qui est équivalent à WideChar maintenant que le type de chaîne par défaut est UnicodeString. Comme l'implémentation de Char est susceptible de changer, il est judicieux d'utiliser la fonction standard SizeOf plutôt qu'une constante codée en dur dans les programmes qui doivent gérer des caractères de tailles différentes.
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 AnsiChar ; 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).

Types booléens

Les quatre types booléens prédéfinis sont Boolean, ByteBool, WordBool et LongBool. Boolean est le type de prédilection. Les autres existent uniquement pour proposer une compatibilité avec d'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 deux octets (un mot), et une variable LongBool occupe quatre octets (deux mots).
Les valeurs booléennes sont désignées par les constantes prédéfinies True et False. Les relations suivantes s'appliquent :


BooleanByteBool, WordBool, LongBool
False < True
False <> True
Ord(False) = 0
Ord(False) = 0
Ord(True) = 1
Ord(True) <> 0
Succ(False) = True
Succ(False) = True
Pred(True) = False
Pred(False) = True



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.

La remarque précédente porte sur le rang des valeurs booléennes, non pas sur les valeurs mêmes. En 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 solutions suivantes sont utilisables.

 if X <> 0 then ...; 
    { use longer expression that returns Boolean value }
   var OK: Boolean;
   ... 
 if X <> 0 then
   OK := True;
 if OK then ...;

Types énumérés

Un type énuméré définit un ensemble ordonné de valeurs simplement en énumérant 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,Ord(Club) renvoie 0, Ord(Diamond) renvoie 1, etc.
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)

Mais Click est également le nom d'une méthode définie dans TControl et dans tous les objets de CLX qui en dérivent. Donc, si vous écrivez une application et écrivez un gestionnaire d'événement de la forme suivante :

 procedure TForm1.DBGridEnter(Sender: TObject);
  var
     Thing: TSound;
     begin 
       ... 
       Thing := Click;
     end;


vous obtenez 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 de 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 est néanmoins de choisir des noms de constantes qui ne risquent pas de rentrer 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 une déclaration de variable comme nom de type :

var MyCard: (Club, Diamond, Heart, Spade);

Mais si vous déclarez MyCard de cette manière, vous ne pouvez déclarer d'autres variables dans la même portée en utilisant ces identificateurs de constantes. Par conséquent :

 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 dont les rangs sont attribués explicitement


Par défaut, le rang des valeurs énumérées commence à 0 et suit l'ordre des identificateurs de la déclaration de type. Vous pouvez redéfinir cela en attribuant explicitement les rangs de certaines ou de toutes les valeurs de la déclaration. Pour attribuer 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 conversions de types et de routines comme Pred, Succ, Inc et Dec. Dans l'exemple suivant, des valeurs "anonymes" dans l'étendue de Size sont attribuées à la variable X.

var 
  X: Size; 
  X := Small;   // Ord(X) = 5 
  Y := Size(6); // Ord(X) = 6 
  Inc(X);       // Ord(X) = 7

Toute valeur à laquelle un rang n'a pas été attribué de façon explicite a pour rang un de plus que celui de la valeur précédente de la liste. Si aucun rang n'est attribué à la première valeur, son rang est 0. Ici, étant donné la déclaration :

type SomeEnum = (e1, e2, e3 = 1);

SomeEnum prend 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 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 du compilateur.
Les énumérations de portée sont caractérisées en préfixant une référence à un élément de type énuméré avec son nom de type. Par exemple, étant donné l'unité et le programme suivants, vous pouvez voir comment une énumération de portée TMyEnum permet l'affectation de la variable Value où la spécification de "First" seul aurait déclenché une erreur de correspondance de type.
unit Unit1;

interface
type
  TMyEnum = (first, second, third);

implementation

end.


program Project1;
{$APPTYPE CONSOLE}
uses
  SysUtils,
  Unit1 in 'Unit1.pas';
var
  First: Integer;
  Value: TMyEnum;
begin
  try
    Value := TMyEnum.First;
  except
    on E:Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;
end.


Types intervalle

Un type intervalle représente un sous-ensemble de valeurs d'un autre type (appelé le type de base). Toute construction de la forme Low..High, où Low et High sont des expressions constantes du même type scalaire, Low étant inférieur à High, identifie un type 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 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 caractère (des constantes chaîne de longueur 1) pour définir des types intervalles :

type 
 SomeNumbers = -128..127;
 Caps = 'A'..'Z';

Quand vous utilisez des constantes numérique ou caractère pour définir un 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 intervalle est celui qu'elle a dans 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 de l'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 intervalle convertit simplement la valeur dans le type de base. Ainsi :

 type Percentile = 0..99;
 var I: Percentile;
   ... 
   I := 100;

produit une erreur, le code suivant :

 ...
 I := 99;
 Inc(I);

affecte la valeur 100 à I (à moins que la vérification des limites de compilation ne soit activée).

L'utilisation d'expressions constantes dans une définition d'intervalle introduit une difficulté syntaxique. Dans toute déclaration de type, quand le premier caractère significatif après le = 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 par une notation à virgule flottante. Le tableau suivant donne l'étendue et le format de stockage des types réels fondamentaux de la plate-forme Win32.

Types réels fondamentaux Win32


TypeEtendue positive approximativeChiffres significatifsTaille en octets
Real48
2.9e-39 .. 1.7e+38
11-12
6
Single
1.5e-45 .. 3.4e+38
7-8
4
Double
3.4e-4932 .. 1.1e+4932
15-16
8
Extended
3.4e-4932 .. 1.1e+4932
10-20
10
Comp
-263+1 .. 263-1
10-20
8
Currency
-922337203685477.5808.. 922337203685477.5807
10-20
8



Le type générique Real est équivalent, dans son implémentation actuelle, au type Double.

Types réels génériques


TypeEtendue positive approximativeChiffres significatifsTaille en octets
Real
5.0e-324 .. 1.7e+308
15-16
8



Remarque : Le type Real48 sur six octets s'appelait Real dans les versions précédentes du Pascal Objet. Si vous recompilez dans Delphi du code utilisant ce type Real sur six octets ancienne manière, vous pouvez le changer en Real48. Vous pouvez également utiliser la directive de compilation {$REALCOMPATIBILITY ON} qui revient à l'interprétation de Real comme un type sur six octets.

Les remarques suivantes s'appliquent aux types réels fondamentaux :


  • Real48' est conservé pour la compatibilité descendante. Comme son format de stockage n'est pas géré naturellement par l'architecture des processeurs Intel, ce type produit des performances plus mauvaises que les autres types à virgule flottante.
  • Extended propose une meilleure précision que les autres types réels, mais il est moins portable. Evitez d'utiliser Extended si vous créez des fichiers de données qui doivent être partagés sur plusieurs plates-formes.
  • 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 scalaire. 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é ascendante. Utilisez le type Int64 pour de meilleures performances.
  • Currency est un type de données à virgule fixe qui limite les erreurs d'arrondi dans les calculs monétaires. Sur la plate-forme Win32, il est stocké dans un entier sur 64 bits, les quatre chiffres les moins significatifs représentant implicitement les chiffres après la virgule. Quand il est combiné avec d'autres types réels dans des affectations et des expressions, les valeurs Currency sont automatiquement divisées ou multipliées par 10000.

Aucun commentaire: