![]() |
|
Marco Cantù
Traduit de l'anglais par Iannis Papageorgiadis [email protected] |
Chapitre 3
|
Le langage Pascal était, à l'origine, basé sur quelques notions simples devenues très courantes dans les langages de programmation. La première notion est celle de type de données. Le type détermine les valeurs que peut avoir une variable et les opérations que l'on peut effectuer sur elle. Le concept de type est plus fort en Pascal qu'en C, où les types arithmétiques sont souvent interchangeables, et plus fort que dans les versions initiales du BASIC où un tel concept n'existait pas.
var Valeur: Integer; EstCorrect: Boolean; A, B: Char;Le mot réservé var peut être utilisé à plusieurs endroits du code, par exemple au début du code d'une fonction ou d'une procédure, pour déclarer des variables locales à la routine, ou à l'intérieur d'une unité pour déclarer des variables globales. Après le mot var arrive la liste des noms de variables, suivie du caractère deux points et du nom du type de données. Vous pouvez écrire plusieurs noms sur une seule ligne comme dans la dernière instruction ci-dessus.
Une fois que l'on a défini une variable d'un type donné, on ne peut effectuer sur elle que les opérations permises par son type de données. Par exemple, on peut utiliser une valeur booléenne dans un test et une valeur entière dans une expression numérique. On ne peut pas mélanger booléens et entiers (comme on peut le faire dans le langage C).
En utilisant des assignations simples, nous pouvons écrire le code suivant :
Valeur := 10;
EstCorrect := True;Mais l'instruction suivante n'est pas correcte, parce que les deux variables sont de types différents
Valeur := EstCorrect; // erreurSi vous essayez de compiler ce code, Delphi aboutit à une erreur de compilation avec la description : Types incompatibles: 'Boolean' et 'Integer'. De telles erreurs sont habituellement des erreurs de programmation parce que assigner la valeur True ou la valeur False à une variable de type entier n'a aucun sens. Il ne faut pas blâmer Delphi pour ces erreurs. Il vous avertit simplement qu'il y a quelque chose d'incorrect dans le code.
Bien sûr, il est souvent possible de convertir la valeur d'une variable d'un type à un autre. Dans certains cas, cette conversion est automatique, mais habituellement vous devez faire appel à une fonction spécifique qui change la représentation interne de la donnée.
En Delphi, on peut assigner une valeur initiale à une variable globale lors de sa déclaration. Par exemple, on peut écrire :
var Valeur: Integer = 10; Correct: Boolean = True;Cette technique d'initialisation fonctionne uniquement pour les variables globales et non pas pour les variables déclarées à l'intérieur de l'étendue d'une procédure ou d'une méthode.
const Mille = 1000; Pi = 3.14; AuthorName = 'Marco Cantù';Delphi détermine le type de données de la constante en fonction de sa valeur. Dans l'exemple ci-dessus, la constante Mille est considérée comme étant de type SmallInt, le plus petit type entier qu'il possède. Si vous souhaitez dire à Delphi d'utiliser un type spécifique, vous pouvez simplement ajouter dans la déclaration le nom du type, comme dans
const Mille: Integer = 1000;Lorsque on déclare une constante, le compilateur peut choisir d'allouer un emplacement mémoire à la constante et y placer sa valeur, ou dupliquer la valeur actuelle chaque fois que la constante est utilisée. Cette seconde approche est utilisée particulièrement pour les constantes simples.
const AuthorName = 'Marco Cantù';à partir de Delphi 3 vous pouvez écrire
resourcestring AuthorName = 'Marco Cantù';Dans les deux cas vous êtes en train de définir une constante; c'est-à-dire une valeur qui ne changera pas pendant l'exécution du programme. La différence réside uniquement dans l'implémentation. Une constante chaîne définie avec la directive resourcestring est stockée dans les ressources du programme dans une table de chaînes.
Pour illustrer cette possibilité, voyez l'exemple ResStr, qui possède un bouton avec le code suivant:
resourcestring AuthorName = 'Marco Cantù'; BookName = 'Essential Pascal'; procedure TForm1.Button1Click(Sender: TObject); begin ShowMessage (BookName + #13 + AuthorName); end;L'affichage des deux chaînes apparaît sur des lignes séparées vu que que les chaînes sont séparées par le caractère retour chariot (indiqué par sa valeur numérique dans la constante numérique #13).
Ce qu'il y a d'intéressant dans ce programme, c'est que si vous
l'examinez avec un explorateur de ressources (il y en a un de disponible
parmi les exemples compris dans Delphi), vous verrez les nouvelles chaînes
parmi les ressources. Ceci signifie que les chaînes ne font pas partie
du code compilé mais qu'elles sont stockées dans une zone
séparée du fichier exécutable (du fichier EXE).
Delphi inclut également un type de données non typé, appelé variant (cfr. Chapitre 10). Assez curieusement, un variant est un type sans contrôle de type propre. Il a été introduit en Delphi 2 pour la gestion des objets OLE Automation.
Les trois types ordinaux les plus importants sont Integer, Boolean et Char (caractère). Cependant, il existe un certain nombre d'autres types apparentés qui ont la même signification mais une représentation interne et un intervalle de valeurs différents. La table ci-dessous donne la liste des types ordinaux de données pour la représentation des nombres.
Table 3.1 : Types de données ordinaux pour les nombres
Taille | Signé
Intervalle |
Non signé
Intervalle |
---|---|---|
8 bits | ShortInt
-128 .. 127 |
Byte
0 .. 255 |
16 bits | SmallInt
-32768 .. 32767 |
Word
0 .. 65535 |
32 bits | LongInt
-2.147.483.648 .. 2.147.483.647 |
LongWord (depuis Delphi 4)
0 .. 4.294.967.295 |
64 bits | Int64 | |
16/32 bits | Integer | Cardinal |
Le dernier groupe (noté 16/32) indique les valeurs ayant une représentation différente dans les versions 16 et 32 bits de Delphi. Integer et Cardinal sont fréquemment utilisés car ils correspondent à la représentation des nombres originelle dans le CPU.
Un autre type nouveau introduit par Delphi 4 est le type Int64, qui représente des entiers jusqu'à 18 chiffres. Ce nouveau type est entièrement supporté par quelques routines de type ordinal (comme High et Low), de type numérique (comme Inc et Dec), ainsi que par quelques routines de conversion de chaînes (comme IntToStr). Pour la conversion opposée, d'un string à un nombre, il existe deux nouvelles fonctions spécifiques : StrToInt64 et StrToInt64Def .
Les constantes caractère peuvent être représentées par leur notation symbolique, par exemple 'k', ou par une notation numérique, par exemple #78. Pour exprimer ce dernier cas, on peut également utiliser la fonction Chr, comme dans Chr(78).
Il est généralement préférable d'utiliser la notation symbolique lorsque'on indique des lettres, des chiffres, ou des symboles. Par contre lorsqu'on se réfère à des caractères spéciaux, on utilisera généralement la notation numérique. La liste ci-dessous comprend quelques caractères spéciaux parmi les plus souvent utilisés:
FIGURE 3.1: L'exemple Range affiche des informations à propos des types de données ordinaux (ici des entiers).
Le programme Range est basé sur une simple fiche, qui comporte six boutons (nommés chacun en fonction d'un type de données ordinal) et quelques libellés (Labels), comme on le voit à la figure 3.1. Quelques libellés sont utilisés pour contenir du texte statique, d'autres pour afficher l'information à propos du type correspondant à chaque pression sur un bouton.
Chaque fois que vous cliquez sur un des boutons de droite, le programme met à jour les libellés. Les divers libellés indiquent le type de données, le nombre de bytes utilisés, ainsi que les valeurs minimum et maximum que le type de données peut stocker. Chaque bouton possède sa propre méthode réponse à l'événement OnClick puisque le code utilisé pour calculer les trois valeurs est légèrement différent d'un bouton à l'autre. Voici, par exemple, le code source de l'événement OnClick pour le bouton Integer (BtnInteger):
procedure TFormRange.BtnIntegerClick(Sender: TObject); begin LabelType.Caption := 'Integer'; LabelSize.Caption := IntToStr (SizeOf (Integer)); LabelMax.Caption := IntToStr (High (Integer)); LabelMin.Caption := IntToStr (Low (Integer)); end;Si vous avez un peu d'expérience de programmation en Delphi, vous pouvez examiner le code source du programme pour comprendre comment il fonctionne. Pour les débutants, il suffit de noter l'utilisation des trois fonctions: SizeOf, High, et Low. Les résultats des deux dernières fonctions sont des ordinaux du même genre (ici des entiers), et le résultat de la fonction SizeOf est toujours un entier. La valeur retournée par chacune de ces fonctions est, pour commencer, convertie en une chaîne à l'aide de la fonction IntToStr, et elle est ensuite copiée dans les titres des trois libellés.
Les méthodes associées aux autres boutons sont fort semblables à celle décrite ci-dessus. La seule différence réside dans le type de données passées par paramètre aux différentes fonctions. La figure 3.2 donne le résultat de l'exécution de ce même programme sous Windows 95 après sa re-compilation avec la version Delphi 16 bits. En comparant les figures 3.1 et 3.2 on peut voir la différence entre les types de données Integer en 16 bits et en 32 bits.
FIGURE 3.2 : Le résultat de la version 16 bits de l'exemple Range, montrant de nouveau l'information à propos des entiers.
La taille du type Integer varie en fonction du CPU et du système d'exploitation que vous utilisez. En Windows 16 bits, une variable Integer occupe deux octets (bytes). En Windows 32 bits, un Integer occupe quatre bytes. C'est la raison pour laquelle, lorsque vous recompilez l'exemple Range, vous obtenez un affichage différent.
Les deux représentations différentes du type Integer ne posent pas de problème aussi longtemps que votre programme ne fait pas de suppositions à propos de la taille des entiers. S'il vous arrive de sauver un Integer dans un fichier utilisant une version et de le rappeler avec une autre, vous aurez quelques problèmes. Dans un tel cas, il faut choisir un type de données indépendant de la plate-forme (comme LongInt ou SmallInt). Pour les calculs mathématiques ou pour du code générique il vaut mieux se baser sur la représentation standard pour la plate-forme spécifique -- c'est -à - dire utiliser le type Integer -- parce que le CPU le préfère. Le type Integer devrait être votre premier choix lors de la manipulation des nombres entiers. N'utilisez une représentation différente que lorsqu'il y a une raison impérieuse de le faire.
Table 3.2 : Routines système pour les types ordinaux
Routine | But |
---|---|
Dec | Décrémente la variable passée par paramètre de un ou de la valeur du deuxième paramètre facultatif. |
Inc | Incrémente la variable passée par paramètre de un ou de la valeur spécifiée. |
Odd | Retourne True si l'argument est un nombre impair. |
Pred | Retourne la valeur précédant dans l'ordre la valeur de
l'argument et déterminée par le type de données |
Succ | Retourne la valeur suivante de l'argument : la valeur suivante. |
Ord | Retourne un nombre indiquant l'ordre de l'argument dans l'ensemble des valeurs du type de données. |
Low | Retourne la plus petite valeur dans l'intervalle du type ordinal passé par paramètre. |
High | Retourne la plus grande valeur dans l'intervalle du type de données ordinal. |
En Delphi 2 et Delphi 3, le type Real avait la même définition que dans la version 16 bits; il s'agissait d'un type 48-bits. Mais son utilisation a été désapprouvée par Borland, qui suggérait par contre d'utiliser les types Single, Double et Extended. Cette suggestion s'explique par le fait que l'ancien format 6-bytes n'est ni supporté par le CPU d' Intel ni listé parmi les types réels officiels de IEEE. Pour surmonter complètement le problème, Delphi 4 modifie la définition du type Real pour représenter un nombre en virgule flottante 8-bytes (64 bits) standard.
Outre l'avantage d'utiliser une définition standard, cette modification permet aux composants de publier des propriétés basées sur le type Real, ce que Delphi 3 ne permettait pas. Au nombre des inconvénients pourraient figurer les problèmes de compatibilité. Si nécessaire, on évitera la possibilité d'incompatibilité en s'en tenant aux définitions du type de Delphi 2 et Delphi 3; on utilisera pour cela l'option de compilation suivante :
{$REALCOMPATIBILITY ON}Il existe également deux types de données étranges : Comp décrit de très grands entiers utilisant 8 bytes (il peut contenir des nombres de 18 chiffres), et Currency (non disponible en Delphi 16 bits) qui indique une valeur à virgule fixe avec quatre décimales, et a la même représentation en 64 bits que le type Comp.
On ne peut pas construire un programme semblable à celui de l'exemple Range avec les types réels, parce qu'on ne peut pas utiliser les fonctions High, Low ou Ord avec des variables de type réel. Les types réels représentent (théoriquement) un ensemble infini de nombres; les types ordinaux représentent un ensemble fini de valeurs.
Remarque : Expliquons mieux la chose. Si vous avez l'entier 23, vous pouvez déterminer quelle est la valeur suivante. Les Integer sont finis (ils ont un intervalle déterminé ainsi qu'un ordre). Les nombres à virgule flottante sont infinis même à l'intérieur d'un petit intervalle, et ils n'ont pas d'ordre: en fait, combien de valeurs y a-t-il entre 23 et 24 ? Et quel est le nombre qui suit 23,46 ? Est-ce 23,47, 23,461 ou 23,4601 ? Difficile à dire!
Pour cette raison, s'il est sensé de demander la position ordinale du caractère w dans l'intervalle du type de données Char, il est par contre insensé de poser la même question pour le nombre 7143,1562 dans l'intervalle d'un type de données à virgule flottante. Bien que vous puissiez évidemment savoir si un nombre réel est supérieur à un autre, il est insensé de demander le nombre de nombres qui précèdent un nombre donné (c'est le sens de la fonction Ord).
Les types réels jouent un rôle limité dans la partie
interface utilisateur du code (du côté de Windows), mais ils
sont entièrement supportés par Delphi, y compris le point
de vue base de données. Le support du standard IEEE des types à
virgule flottante fait que le langage Pascal Objet est parfaitement approprié
pour la grande variété de programmes qui exigent des calculs
numériques. Si vous vous intéressez à cet aspect,
vous trouverez les fonctions arithmétiques prévues par Delphi
dans l'unité système (pour plus de détails, voir l'aide
Delphi).
TDateTime n'est pas un type prédéfini compris par le compilateur, mais il est défini dans l'unité system comme suit :
type TDateTime = type Double;Utiliser le type TDateTime est très aisé, parce que Delphi comprend une série de fonctions qui opèrent sur ce type. Vous trouverez une liste de ces fonctions dans la Table 3.2.
Table 3.3 : Routines système pour le type TDateTime
Routine | Description |
---|---|
Now | Retourne la date et l'heure courantes dans une seule valeur. |
Date | Retourne uniquement la date courante. |
Time | Retourne uniquement l'heure courante. |
DateTimeToStr | Convertit une valeur date-heure en une chaîne, en utilisant le formatage par défaut; pour un meilleur contrôle de la conversion utilisez par contre la fonction FormatDateTime |
DateTimeToString | Copie la date et l'heure dans un tampon chaîne, avec le formatage par défaut. |
DateToStr | Convertit la partie date d'une valeur de type TDateTime en une chaîne. |
TimeToStr | Convertit la partie heure d'une valeur de type TDateTime en une chaîne. |
FormatDateTime | Formate une date et une heure en utilisant le format spécifique; vous pouvez spécifier les valeurs que vous souhaitez voir et le format utilisé, offrant un format chaîne complexe. |
StrToDateTime | Convertit une chaîne contenant une information date temps en une valeur TDateTime, soulevant une exception si la chaîne contient un format erroné. |
StrToDate | Convertit une chaîne avec une valeur date en un format TDateTime. |
StrToTime | Convertit une chaîne avec une valeur temps en un format TDateTime. |
DayOfWeek | Retourne le nombre correspondant au jour de la semaine de la valeur TDateTime passée en paramètre. |
DecodeDate | Récupère les valeurs année, mois et jour au départ d'une valeur date. |
DecodeTime | Sépare la valeur d' une valeur temps. |
EncodeDate | Renvoie les valeurs année, mois et jour dans une valeur TDateTime. |
EncodeTime | Renvoie les valeurs heure, minute, seconde et milli-seconde dans une valeur TDateTime. |
Voici le code relatif à l'événement OnCreate de la fiche:
procedure TFormTimeNow.FormCreate(Sender: TObject); begin StartTime := Now; ListBox1.Items.Add (TimeToStr (StartTime)); ListBox1.Items.Add (DateToStr (StartTime)); ListBox1.Items.Add ('Pour le temps écoulé presser le bouton'); end;La première instruction est un appel à la fonction Now qui retourne la date et heure courantes. Cette valeur est stockée dans la variable StartTime, déclarée en tant que variable globale comme suit
var FormTimeNow: TFormTimeNow; StartTime: TDateTime;Nous avons ajouté une deuxième déclaration, puisque la première a été prévue par Delphi. Par défaut elle est comme suit
var Form1: TForm1;En changeant le nom de la fiche, la déclaration est automatiquement mise à jour. Utiliser des variables globales n'est pas vraiment la meilleure approche. Il aurait été préférable d'utiliser un champ privé de la classe de la fiche, un sujet lié à la programmation orientée objet et traité dans Mastering Delphi 4.
Les trois instructions suivantes ajoutent trois éléments dans le composant ListBox de la fiche, avec le résultat que l'on peut voir dans la figure 3.3. La première ligne contient la partie temps de la valeur TDateTime convertie en une chaîne, la deuxième la partie date de cette même valeur. A la fin le code ajoute seulement un rappel.
FIGURE 3.3: Le résultat de l'exemple TimeNow au démarrage.
La troisième chaîne est remplacée par le programme
lorsque l'utilisateur clique sur le bouton Temps écoulé
procedure TFormTimeNow.ButtonElapsedClick(Sender: TObject); var StopTime: TDateTime; begin StopTime := Now; ListBox1.Items [2] := FormatDateTime ('hh:nn:ss', StopTime - StartTime); end;Ce code récupère le nouveau temps et calcule la différence à partir de la valeur du temps stocké lors du démarrage du programme. Puisque nous devons utiliser une valeur que nous avons calculée dans un gestionnaire d'événement différent, nous devons la stocker dans une variable globale. Il existe en fait de meilleures possibilités, basées sur les classes.
DateSeparator: Char; ShortDateFormat: string; LongDateFormat: string; TimeSeparator: Char; TimeAMString: string; TimePMString: string; ShortTimeFormat: string; LongTimeFormat: string; ShortMonthNames: array[1..12] of string; LongMonthNames: array[1..12] of string; ShortDayNames: array[1..7] of string; LongDayNames: array[1..7] of string;plus des constantes globales liées au formatage des nombres à virgule flottante et monétaires. Vous trouverez la liste complète dans le fichier d'aide de Delphi à la rubrique Variables de format monétaire et date/heure.
Parmi les types de données Windows, le type le plus plus important est représenté par les handles. Nous en discuterons au chapitre 9.
var N: Integer; C: Char; B: Boolean; begin N := Integer ('X'); C := Char (N); B := Boolean (0);Vous pouvez effectuer un transtypage entre types de données de même taille. Le transtypage entre types ordinaux ou entre types réels est souvent sans risque; mais vous pouvez également l'effectuer entre types pointeur (et aussi objets) tant que vous savez ce que vous êtes en train de faire.
Transtyper est, cependant, une pratique de programmation dangereuse parce qu'elle permet d'accéder à une valeur comme si elle représentait autre chose. Puisque les représentations internes des types de données ne s'accordent généralement pas, on risque d'aboutir à des erreurs difficiles à détecter.
La deuxième possibilité est d'utiliser une routine de conversion de type. Les routines pour les différents types de conversion sont résumées dans la table 3.3. Quelques-unes de ces routines s'appliquent à des types de données dont il sera question dans les sections suivantes. Remarquez que le tableau ne comprend pas de routines pour les types spéciaux (comme TDateTime ou variant) ou de routines spécialement destinées au formatage, comme les puissantes Format et FormatFloat.
Table 3.4: Routines système pour la conversion de type
Routine | Description |
---|---|
Chr | Convertit un numéro ordinal en un caractère ANSI. |
Ord | Convertit une valeur de type ordinal en un nombre indiquant son ordre. |
Round | Convertit une valeur de type réel en une valeur de type Integer, en arrondissant sa valeur. |
Trunc | Convertit une valeur de type réel en une valeur de type Integer, en tronquant sa valeur. |
Int | Retourne la partie entière de l'argument valeur à virgule flottante. |
IntToStr | Convertit un nombre en une chaîne. |
IntToHex | Convertit un nombre en une chaîne contenant la représentation hexadécimale du nombre. |
StrToInt | Convertit une chaîne en un nombre, soulevant une exception si la chaîne ne représente pas un entier valide. |
StrToIntDef | Convertit une chaîne en un nombre, utilisant une valeur par défaut si la chaîne n'est pas correcte. |
Val | Convertit une chaîne en un nombre (routine traditionnelle de Turbo Pascal, disponible pour la compatibilité). |
Str | Convertit un nombre en une chaîne, en utilisant des paramètres de formatage (routine traditionnelle de Turbo Pascal, disponible pour la compatibilité). |
StrPas | Convertit une chaîne à zéro terminal en une chaîne style Pascal. Cette conversion est faite automatiquement pour AnsiString dans Delphi 32 bits. (Voir la section sur les chaînes plus loin dans ce chapitre). |
StrPCopy | Copie une chaîne style Pascal dans une chaîne à zéro terminal. Cette conversion est effectuée avec un simple typage PChar en DElphi 32 bits. |
StrPLCopy | Copie une partie d'une chaîne style Pascal dans une chaîne à zéro terminal. |
FloatToDecimal | Convertit une valeur virgule flottante en un enregistrement contenant sa représentation décimale (exposant, chiffres, signe). |
FloatToStr | Convertit une valeur virgule flottante en sa représentation chaîne en utilisant le format par défaut. |
FloatToStrF | Convertit une valeur virgule flottante en sa représentation chaîne en utilisant le format spécifié. |
FloatToText | Convertit une valeur virgule flottante dans une chaîne tampon en utilisant le format spécifié. |
FloatToTextFmt | Comme les routines précédentes, elle copie la valeur virgule flottante dans une chaîne tampon en utilisant le format spécifié. |
StrToFloat | Convertit la chaîne Pascal donnée en une valeur virgule flottante. |
TextToFloat | Convertit la chaîne à zéro terminal donnée en une valeur virgule flottante. |
Prochain chapitre: Les
types de données définies par l'utilisateur
© Copyright Marco Cantù, Wintech Italia Srl 1995-99 |