Logo

Marco Cantù
L'essentiel sur Pascal

Traduit de l'anglais par Iannis Papageorgiadis ipapag@village.uunet.be

Chapitre 8
La mémoire

Ce chapitre doit traiter de la gestion de la mémoire, des diverses zones mémoire, et introduire les tableaux dynamiques. Mais pour l'instant, seule cette dernière partie est disponible.

Les tableaux dynamiques de Delphi 4

Traditionnellement, le langage Pascal a toujours disposé de tableaux de taille fixe. Lorsqu'on déclare un type de données utilisant une structure de tableau, on doit spécifier le nombre de composantes du tableau. Comme les programmeurs expérimentés le savent probablement, quelques techniques existent qui peuvent être utilisées pour implémenter des tableaux dynamiques, notamment en utilisant des pointeurs et en allouant et en libérant manuellement la mémoire nécessaire.

Delphi 4 introduit une implémentation très simple des tableaux dynamiques, sur le modèle du type dynamique chaîne longue que nous venons de traiter. Comme les chaînes longues, les tableaux dynamiques sont alloués dynamiquement et comportent un compteur de références mais ils n'offrent pas une technique copie-par-écriture (copy-on-write). Ce n'est pas un grand problème puisqu'on peut désallouer un tableau en plaçant sa variable à nil.

Maintenant on peut simplement déclarer un tableau sans spécifier le nombre de composantes et l'allouer avec une taille donnée en utilisant la procédure SetLength. La même procédure est aussi utilisée pour redimensionner un tableau sans perdre son contenu. Il existe également d'autres procédures orientées-chaîne, comme la fonction Copy qu'on peut utiliser avec des tableaux.

Voici un petit extrait de code, qui souligne le fait qu'il faut et déclarer et allouer de la mémoire pour le tableau avant de pouvoir l'utiliser :

procedure TForm1.Button1Click(Sender: TObject);

var

  Array1: array of Integer;

begin

  Array1 [1] := 100; // erreur

  SetLength (Array1, 100);

  Array1 [99] := 100; // OK

  ...

end;
Comme on indique uniquement le nombre de composantes du tableau, l'indice commence toujours à 0. En Pascal, des tableaux génériques utilisent un indice inférieur différent de zéro et des indices non entiers, deux caractéristiques qui ne sont pas supportées par les tableaux dynamiques. Pour connaître l'état d'un tableau dynamique on peut utiliser les fonction Length, High et Low comme dans tout tableau. Pour les tableaux dynamiques, toutefois, Low retourne toujours 0 et High retourne toujours la longueur moins un. Ceci implique que pour un tableau vide High retourne -1 (valeur bizarre si on y pense, puisqu'elle est inférieure à celle retournée par Low).
 
Figure 8.1 : La fiche de l'exemple DynArr
 

Après cette brève introduction, voyons un exemple simple, appelé DynArr et illustré à la figure 8.1. Il est simple en effet, parce que il n'y a rien de très compliqué avec les tableaux dynamiques. Nous l'utiliserons également pour montrer quelques erreurs que les programmeurs pourraient commettre. Le programme déclare deux tableaux globaux et initialise le premier dans le gestionnaire OnCreate :

var

  Array1, Array2: array of Integer;



procedure TForm1.FormCreate(Sender: TObject);

begin

  // allocation

  SetLength (Array1, 100);

end;
Ceci place toutes les valeurs à zéro. Ce code d'initialisation permet de commencer à lire et écrire des valeurs du tableau immédiatement, sans aucune crainte d'erreur mémoire. (En admettant bien sûr que l'on n'essaie pas d'accéder à des composantes au-delà de l'indice maximum du tableau). Pour une meilleure initialisation, le programme possède un bouton qui écrit dans chaque cellule du tableau :
procedure TForm1.btnFillClick(Sender: TObject);

var

  I: Integer;

begin

  for I := Low (Array1) to High (Array1) do

    Array1 [I] := I;

end;
Le bouton Grandir permet de modifier la taille du tableau sans perdre son contenu. Vous pouvez le tester un utilisant le bouton Saisir valeur après avoir pressé le bouton Grandir :
procedure TForm1.btnGrowClick(Sender: TObject);

begin

  // Agrandir en conservant les valeurs existantes

  SetLength (Array1, 200);

end;



procedure TForm1.btnGetClick(Sender: TObject);

begin

  // extraire

  Caption := IntToStr (Array1 [99]);

end;
Le seul code un peu complexe se trouve dans l'événement OnClick du bouton Alias. Le programme copie un tableau dans l'autre avec l'opérateur := en créant effectivement un alias (une nouvelle variable faisant référence à la même zone mémoire). A ce point, toutefois, si on modifie un des tableaux, l'autre est également affecté, vu que tous deux font référence à la même zone mémoire :
procedure TForm1.btnAliasClick(Sender: TObject);

begin

  // alias

  Array2 := Array1;

  // l'un change (les deux changent)

  Array2 [99] := 1000;

  // afficher l'autre

  Caption := IntToStr (Array1 [99]);
La méthode btnAliasClick effectue deux opérations supplémentaires. La première est un test d'égalité des tableaux. Elle ne teste pas les véritables composantes de la structure mais plutôt les zones mémoire auxquelles font référence les tableaux, en vérifiant si les variables sont deux alias du même tableau en mémoire :
procedure TForm1.btnAliasClick(Sender: TObject);

begin

  ...

  if Array1 = Array2 then

    Beep;

  // tronquer le premier tableau

  Array1 := Copy (Array2, 0, 10);

end;
La seconde est un appel à la fonction Copy qui non seulement déplace des données d'un tableau à l'autre mais remplace également le premier tableau par un nouveau créé par la fonction. Le résultat est que la variable Array1 fait maintenant référence à un tableau de 11 composantes, si bien que, en pressant le bouton Saisir valeur ou le bouton Placer valeur, se produit une erreur mémoire et se lève une exception (à moins que vous n'ayez décoché la case Vérification des limites, auquel cas l'erreur persiste mais l'exception n'est pas affichée). Le code du bouton Remplir continue à fonctionner convenablement même après cette modification, puisque les composantes à modifier sont déterminées en utilisant les limites actuelles du tableau.

Conclusion

Momentanément, ce chapitre ne traite que les tableaux dynamiques, lesquels constituent certes un élément important de la gestion de la mémoire, mais ne sont qu'une partie de l'ensemble. Les autres points seront traités plus tard.

La structure de mémoire décrite dans ce chapitre est typique de la programmation Windows, sujet que nous introduirons au chapitre suivant (sans aller jusqu'à présenter complètement l'utilisation de la VCL).

Chapitre suivant : Programmer Windows

© Copyright Marco Cantù, Wintech Italia Srl 1995-99