IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

La programmation Win32 en Virtual Pascal avec OWL


précédentsommairesuivant

II. Les chaînes à zéro terminal

Les chaînes à zéro terminal (AZT en français, ASCIIZ en anglais) sont la norme dans l'API Windows. Comme vous allez en déguster à toutes les sauces tout au long du tutoriel, il est essentiel que vous en maîtrisiez la représentation en mémoire ainsi que les subtilités du langage Pascal qui les concernent.
Une bonne approche de la problématique des chaînes à zéro terminal est déjà à votre disposition : Les Strings et pChar en Delphi par Adrien Reboisson.

II-A. Tableaux de caractères et type pChar

Une chaîne AZT est un tableau (à 1 dimension, un vecteur donc) de caractères terminé par 0.

Le 0 terminal est le caractère #0 (dont le code ANSI vaut 0), à ne pas confondre avec le caractère '0' (dont le code ANSI vaut 48) !

Voici la représentation d'une chaîne AZT en mémoire :

 
Sélectionnez
|'B'|'o'|'n'|'j'|'o'|'u'|'r'| 0 |

Théoriquement, une chaîne AZT n'a aucune limitation de taille, si ce n'est celle de la mémoire du système.
Sa déclaration est celle d'un tableau de caractères :

 
Sélectionnez
Var Chaine : Array [0..10] of Char;

La variable Chaine permet de stocker 10 caractères + le zéro terminal.

En C, il n'y a aucune différence entre un tableau en mémoire et un pointeur vers ce tableau. En Pascal, si ! Sauf pour les chaînes AZT. Un type particulier de pointeur, pChar, permet l'adressage des chaînes AZT et le passage de chaînes AZT comme paramètres aux fonctions de l'API Windows.

1ère notion à retenir : Tableau de caractères et pointeur sur un tableau de caractères sont équivalents.

Il est donc tout-à-fait correct d'écrire les deux instructions suivantes :

 
Sélectionnez
Var Chaine : Array [0..10] of Char;
    pChaine : pChar;
Begin
  (* Les 2 instructions suivantes font la même chose : *)
  pChaine := Chaine;
  pChaine := @Chaine;
End.

Ces deux instructions font exactement la même chose : assigner à pChaine l'adresse de Chaine.

Autre illustration de l'équivalence entre tableau de caractères et pointeur pChar :

 
Sélectionnez
Var Chaine : Array [0..10] of Char;
    pChaine : pChar;
    Caractere : Char;
Begin
  pChaine := Chaine;
  (* Les 2 instructions suivantes font la même chose : *)
  Caractere := Chaine[6];
  Caractere := pChaine[6];
End.

Toujours selon le même principe, voici un exemple de passage d'une chaîne AZT comme paramètre à une procédure :

 
Sélectionnez
Var Chaine : Array [0..10] of Char;
    pChaine : pChar;

Procedure TRAITEMENT (p : pChar);
Begin
  { Traitement }
End;  

Begin
  pChaine := Chaine;
  (* Les 2 instructions suivantes font la même chose : *)
  TRAITEMENT(Chaine);
  TRAITEMENT(pChaine);
End.

2ème notion à retenir : Les constantes tableaux de caractères sont elles-aussi équivalentes aux pointeurs pChar.
3ème notion à retenir : Les paramètres de type chaîne AZT sont passés aux procédures et fonctions par adresse
.

Nous pouvons donc aussi passer une constante tableau de caractères comme paramètre à une procédure :

 
Sélectionnez
TRAITEMENT('hello');

Cette dernière instruction stocke la chaîne de caractères dans le segment de données du programme et passe son adresse à la procédure. Il est important de bien comprendre ce principe.

Il se passe exactement la même chose lorsqu'on assigne une constante tableau de caractères à notre pointeur pChar :

 
Sélectionnez
pChaine := 'tagada tsoin tsoin';

La chaîne est stockée dans le segment de données et son adresse est affectée à pChaine. La chaîne n'est donc aucunement recopiée.

II-A-1. En résumé

  • Tableau de caractères (Array of Char) et pointeur pChar sont équivalents;
  • Constante tableau de caractères ('Bonjour') et pointeur pChar sont équivalents;
  • Les paramètres de type chaîne AZT sont passés aux procédures et fonctions par adresse.

II-B. Opérations sur les chaînes à zéro terminal

L'unité Strings de Virtual Pascal propose pas mal de routines de gestion des chaînes AZT :

 
Sélectionnez
Unit Strings;

Interface

Function StrLen (Str : pChar) : Word;
   (* Retourne la longueur d'une chaîne (0 terminal non compris) *)

Function StrCopy (Dest, Source : pChar) : pChar;
   (* Copie Source dans Dest *)      
Function StrECopy (Dest, Source : pChar) : pChar;
   (* Copie Source dans Dest et retourne un pointeur sur le 0 terminal *)
Function StrLCopy (Dest, Source : pChar; MaxLen : Word) : pChar;
   (* Copie MaxLen caractères de Source vers Dest *)      
Function StrCat (Dest, Source : pChar) : pChar;
   (* Ajoute Source à Dest *)
Function StrLCat (Dest, Source : pChar; MaxLen : Word) : pChar;
   (* Ajoute Source à Dest dans la limite de MaxLen caractères *)
Function StrMove (Dest, Source : pChar; Count: Word) : pChar;
   (* Transfère Count caractères de Source vers Dest *)

Function StrComp (Str1, Str2 : pChar) : Integer;
   (* Compare deux chaînes *)
Function StrIComp (Str1, Str2 : pChar) : Integer;
   (* Compare deux chaînes sans tenir compte des majuscules/minuscules *)
Function StrLComp (Str1, Str2 : pChar; MaxLen : Word) : Integer;
   (* Compare deux chaînes sur une longueur de MaxLen caractères *)
Function StrLIComp (Str1, Str 2: pChar; MaxLen : Word) : Integer;
   (* Compare deux chaînes sur une longueur de MaxLen caractères
      sans tenir compte des majuscules/minuscules *)

Function StrPos (Str1, Str2 : pChar) : pChar;
   (* Retourne l'adresse de la 1ère occurence de Str2 au sein de Str1 *)
Function StrScan (Str : pChar; Chr : Char) : pChar;
   (* Retourne l'adresse de la 1ère occurence du caractère Chr dans Str *)
Function StrRScan (Str : pChar; Chr : Char) : pChar;
   (* Retourne l'adresse de la dernière occurence du caractère Chr dans Str *)
Function StrEnd (Str : pChar) : pChar;
   (* Retourne un pointeur sur le 0 terminal d'une chaîne *)

Function StrUpper (Str : pChar) : pChar;
   (* Convertit une chaîne en majuscules *)
Function StrLower (Str : pChar) : pChar;
   (* Convertit une chaîne en minuscules *)

Function StrPas (Str : pChar) : String;
   (* Convertit une chaîne AZT en String *)
Function StrPCopy (Dest : pChar; Source : String) : pChar;
   (* Copie une String dans une chaîne AZT *)

Function StrNew (Str : pChar) : pChar;
   (* Alloue dynamiquement une copie de chaîne et renvoie son adresse *)
Procedure StrDispose (Str : pChar);
   (* Désalloue une chaîne allouée par StrNew *)

Passons en revue les opérations les plus courantes. Plusieurs des routines présentées ci-après possèdent des variantes, que nous aborderons en temps voulu tout au long du tutoriel.

II-B-1. Assigner une valeur à une chaîne

Pour assigner une valeur à une chaîne AZT, on utilise la fonction StrCopy :

 
Sélectionnez
StrCopy(Chaine,'Bonjour');

Physiquement, la chaîne 'Bonjour' est copiée dans la chaîne AZT. Il y a donc bien deux exemplaires de la chaîne à l'arrivée !

Il peut être tentant, par analogie avec les Strings, d'assigner une valeur à une chaîne AZT comme ceci :

 
Sélectionnez
Chaine := 'Bonjour';   (* Erreur de syntaxe ! *)

Mais le compilateur rejettera cette syntaxe ! Il faut employer StrCopy.

II-B-2. Initialiser une chaîne vide

Pour initialiser une chaîne vide, c'est tout simple : il suffit qu'elle commence par le zéro terminal !

 
Sélectionnez
Chaine[0] := #0;

II-B-3. Tronquer une chaîne

De la même manière, on peut tronquer une chaîne en y insérant un zéro terminal :

 
Sélectionnez
Chaine[5] := #0;

II-B-4. Déterminer la longueur d'une chaîne

Pour déterminer la longueur d'une chaîne AZT, on utilise la fonction StrLen :

 
Sélectionnez
Longueur := StrLen(Chaine);

Cette fonction compte les caractères de la chaîne jusqu'au zéro terminal.

II-B-5. Copier une chaîne

StrCopy sert également à copier une chaîne 1 dans une chaîne 2 :

 
Sélectionnez
StrCopy(Chaine2,Chaine1);

Retenez bien l'ordre des paramètres : destination avant source !
Après exécution de cette instruction, il y a bien deux exemplaires de la chaîne de départ.

Par contre (pour vous torturer un peu les méninges), combien y a-t-il d'exemplaires de la chaîne 1 après exécution de l'instruction suivante ?

 
Sélectionnez
Chaine2 := Chaine1;

Réponse : un seul exemplaire, bien sûr ! Cette instruction ne fait que copier l'adresse de la chaîne 1 dans la chaîne 2 et donc les deux pointeurs pointent sur la même chaîne en mémoire.

II-B-6. Comparer deux chaînes

Pour comparer deux chaînes AZT, vous avez le choix entre StrComp (qui tient compte des majuscules et minuscules) et StrIComp (qui n'en tient pas compte).
La valeur retournée par ces deux fonctions peut être positive, négative ou nulle :

 
Sélectionnez
Var Chaine1 : Array [0..40] of Char;
    Chaine2 : Array [0..30] of Char;
    Resultat : Integer;

Resultat := StrIComp(Chaine1,Chaine2);
if Resultat > 0
   then
     WriteLn('Chaine1 > Chaine2')
   else
     if Resultat < 0
        then
          WriteLn('Chaine1 < Chaine2')
        else
          WriteLn('Chaine1 = Chaine2');

II-B-7. Déterminer la position d'une chaîne dans une autre

Pour déterminer la position d'une chaîne dans une autre, on utilise StrPos.
Alors que la fonction Pos (pour les chaînes à attribut de longueur du Pascal) renvoie un indice, la fonction StrPos retourne un pointeur.

Un petit exemple :

 
Sélectionnez
Program POSITION;

Uses WinCRT,Strings;

Var Chaine : Array [0..40] of Char;
    Extrait : Array [0..15] of Char;
    Adresse : pChar;

Begin
  StrCopy(Chaine,'Salut la compagnie !');
  StrCopy(Extrait,'compagnie');
  Adresse := StrPos(Chaine,Extrait);
  WriteLn(Adresse);
  WriteLn('Indice de l''extrait : ',Adresse - Chaine);
End.

Ne tenez pas compte pour l'instant de la déclaration de l'unité WinCRT : elle fera l'objet du chapitre suivant. Compilez et exécutez ce petit programme; vous obtiendrez quelque chose comme ceci :

Image non disponible

Tout cela mérite quelques explications.
Tout d'abord, la variable Adresse retournée par l'instruction

 
Sélectionnez
Adresse := StrPos(Chaine,Extrait);

porte bien son nom puisqu'elle contient l'adresse de la chaîne 'compagnie' dans la chaîne 'Salut la compagnie !'. Pour bien le prouver, l'instruction

 
Sélectionnez
WriteLn(Adresse);

affiche à l'écran : compagnie !, c'est-à-dire à partir de l'adresse Adresse jusqu'au zéro terminal.

Enfin, notre dernière instruction soustrait l'adresse de la chaîne de départ à l'adresse trouvée par StrPos pour donner l'indice de l'extrait. Non, il n'y a pas d'erreur : l'indice du 1er caractère est 0 et non 1 comme pour les chaînes à attribut de longueur : l'indice affiché est bien 9 et non 10.

II-B-8. Concaténer deux chaînes

Pour concaténer deux chaînes AZT, il existe l'instruction StrCat :

 
Sélectionnez
Var Chaine1 : Array [0..40] of Char;
    Chaine2 : Array [0..30] of Char;

StrCat(Chaine1,Chaine2);

L'instruction ci-dessus ajoute le contenu de Chaine2 à Chaine1.

Que se passe-t-il si le contenu des deux chaînes mis bout à bout dépasse la taille limite de Chaine1 ? Un beau plantage à courte échéance !

Retenez bien ceci : la plupart des routines de l'unité Strings n'effectuent aucune vérification de débordement !

Il incombe donc au programmeur de prendre ses précautions. Dans le cas présent, une de ces précautions peut être de choisir la variante StrLCat :

 
Sélectionnez
Var Chaine1 : Array [0..40] of Char;
    Chaine2 : Array [0..30] of Char;

StrLCat(Chaine1,Chaine2,SizeOf(Chaine1) - 1);

Traduite en langage courant, cette instruction signifie : "ajouter à Chaine1 le contenu de Chaine2 en veillant à ne pas dépasser la taille maximale de Chaine1".

II-B-9. Conversion entre chaînes AZT et Strings

Il est possible de convertir une chaîne AZT en String à l'aide de la fonction StrPas :

 
Sélectionnez
Program CONVERS1;

Uses Strings;

Var s : String [7];
    c : Array [0..7] of Char;

Begin
  StrCopy(c,'Bonjour');
  s := StrPas(c);
End.

La conversion inverse se fait avec la fonction StrPCopy :

 
Sélectionnez
Program CONVERS2;

Uses Strings;

Var s : String [7];
    c : Array [0..7] of Char;

Begin
  s := 'Bonjour';
  StrPCopy(c,s);
End.

II-B-10. Allocation dynamique de chaînes AZT

L'allocation dynamique de chaînes AZT permet de tirer parti de la quantité de mémoire disponible dans le tas global de Windows, bien supérieure à la capacité d'un segment de données ou d'un segment de pile.
La fonction StrNew permet d'allouer une chaîne AZT :

 
Sélectionnez
Var p : pChar;

p := StrNew('Allocation dans le tas');

Cette instruction alloue l'espace nécessaire dans le tas, y copie la chaîne et retourne l'adresse du bloc alloué. Il y a donc deux copies de la chaîne : une dans le segment de données et l'autre dans le tas.

Le principe de l'allocation dans le tas est économe en mémoire car n'est alloué que l'espace nécessaire pour stocker une copie de chaîne avec le zéro terminal.
Illustration :

 
Sélectionnez
Var Chaine : Array [0..255] of Char;
    p : pChar;

Begin
  StrCopy(Chaine,'Allocation dans le segment de donn'#233'es');
  p := StrNew(Chaine);
  WriteLn(p);
  StrDispose(p);
End.

StrNew alloue dans le tas l'espace nécessaire pour stocker non pas 255 caractères (+ le zéro terminal) mais seulement 36 caractères (+ le zéro terminal) !
Notez que si la longueur de la chaîne est nulle ou s'il n'y a plus assez de mémoire dans le tas pour allouer la chaîne, StrNew renverra Nil.
Remarquez que p peut parfaitement être utilisé avec l'instruction WriteLn pour afficher la chaîne allouée dans le tas.

La fonction qui permet de libérer dans le tas l'espace alloué à une chaîne AZT est StrDispose.


précédentsommairesuivant

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2005-2007 Jean-Luc Gofflot. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.