Section courante

A propos

Section administrative du site

Procédures et fonctions

Les procédures et fonctions vous permettent d'imbriquer des blocs supplémentaires dans le bloc de programme principal. Chaque déclaration de procédure ou de fonction a un entête suivi d'un bloc. Une procédure est activée par une instruction de procédure ; une fonction est activée par l'évaluation d'une expression contenant son appel et renvoie une valeur à cette expression.

Déclarations de procédure

Une déclaration de procédure associe un identifiant à un bloc en tant que procédure ; cette procédure peut ensuite être activée par une instruction de procédure.

L'entête de procédure nomme l'identificateur de la procédure et spécifie les paramètres formels (le cas échéant). Une procédure est activée par une instruction de procédure, indiquant l'identificateur de la procédure et les paramètres réels, le cas échéant. Les instructions à exécuter à l'activation sont notées dans la partie instruction du bloc de la procédure. Si l'identificateur de la procédure est utilisé dans une instruction de procédure au sein du bloc de la procédure, la procédure est exécutée de manière récursive (elle s'appelle lors de l'exécution). Voici un exemple de déclaration de procédure :

  1. Procedure NumString(N:Integer;Var S:String);
  2. Var
  3.  V:Integer;
  4. Begin
  5.  V:=Abs(N);
  6.  S :='';
  7.  Repeat
  8.   S := Chr(N mod 10 + Ord('0')) + S;
  9.   N := N div 10;
  10.  Until N = 0;
  11.  If N < 0 Then S := '-' + S;
  12. End;

Déclarations NEAR et FAR

Le Turbo Pascal prend en charge deux modèles d'appel de procédure et de fonction : NEAR (proche) et FAR (éloigné). En termes de taille de code et de vitesse d'exécution, le modèle d'appel NEAR est le plus efficace, mais les procédures et fonctions proches ne peuvent être appelées qu'à partir du module dans lequel elles sont déclarées. D'un autre côté, les procédures et fonctions FAR peuvent être appelées à partir de n'importe quel module, mais le code pour un appel FAR est légèrement moins efficace.

Le compilateur sélectionne automatiquement le modèle d'appel correct en fonction de la déclaration d'une procédure ou d'une fonction : Les procédures et fonctions déclarées dans la partie interface d'une unité utilisent le modèle d'appel à distance - elles peuvent être appelées à partir d'autres modules. Les procédures et fonctions déclarées dans un programme ou dans la partie implémentation d'une unité utilisent le modèle d'appel proche - elles ne peuvent être appelées qu'à partir de ce programme ou de cette unité.

À certaines fins, une procédure ou une fonction peut être requise pour utiliser le modèle d'appel à distance. Par exemple, si une procédure ou une fonction doit être affectée à une variable procédurale, elle doit utiliser le modèle d'appel à distance. La directive du compilateur $F peut être utilisée pour remplacer la sélection automatique du modèle d'appel du compilateur. Les procédures et fonctions compilées dans l'état {$F+} utilisent toujours le modèle d'appel à distance ; dans l'état {$F-}, le compilateur sélectionne automatiquement le bon modèle. L'état par défaut est {$F-}.

Pour forcer un modèle d'appel spécifique, une déclaration de procédure ou de fonction peut éventuellement spécifier une directive NEAR ou FAR avant le bloc - si une telle directive est présente, elle remplace le paramètre de la directive du compilateur $F ainsi que la sélection automatique du modèle d'appel du compilateur.

Déclarations d'interruption

Facultativement, une déclaration de procédure peut spécifier une directive d'interruption avant le bloc ; la procédure est alors considérée comme une procédure d'interruption. Pour l'instant, notez que les procédures d'interruption ne peuvent pas être appelées à partir d'instructions de procédure et que chaque procédure d'interruption doit spécifier une liste de paramètres comme celle-ci :

  1. Procedure MonInterruption(Flags,ES,IP,AX,BX,CX,DX,SI,DI,DS,ES,BP:Word);Interrupt;

La liste de paramètres n'a pas besoin de correspondre parfaitement à cette syntaxe ; il peut être plus court et utiliser des noms différents, mais le contenu du registre est transmis dans l'ordre indiqué ci-dessus.

Les déclarations FORWARD

Une déclaration de procédure ou de fonction spécifiant la directive FORWARD au lieu d'un bloc est une déclaration FORWARD. Quelque part après cette déclaration, la procédure doit être définie par une déclaration de définition. La déclaration de définition peut omettre la liste des paramètres formels et le résultat de la fonction, ou elle peut éventuellement le répéter. Dans ce dernier cas, l'entête de la déclaration de définition doit correspondre exactement à l'ordre, aux types et aux noms des paramètres, ainsi qu'au type du résultat de la fonction, le cas échéant.

La déclaration FORWARD et la déclaration de définition doivent apparaître dans la même partie de déclaration de procédure et de fonction. D'autres procédures et fonctions peuvent être déclarées entre elles et elles peuvent appeler la procédure déclarée en aval. Par conséquent, la récursivité mutuelle est possible. La déclaration préalable et la déclaration de définition constituent une déclaration complète de procédure ou de fonction. La procédure ou la fonction est considérée comme déclarée lors de la déclaration anticipée. Voici un exemple de déclaration anticipée :

  1. Procedure Sylvain(M,N:Integer);Forward;
  2.  
  3. Procedure Sophie(X,Y:Real);Begin
  4.  Sylvain(4, 5);
  5. End;
  6.  
  7. Procedure Sylvain;Begin
  8.  Sophie(8.3, 2.4);
  9. End;

Une déclaration de définition de procédure ou de fonctions peut être une déclaration externe ou en assembleur ; cependant, il ne peut pas s'agir d'une déclaration proche, éloignée, d'interruption ou en ligne ou d'une autre déclaration avant.

Déclarations EXTERNAL

Les déclarations EXTERNAL vous permettent d'interfacer avec des procédures et des fonctions compilées séparément écrites en langage assembleur. Le code externe doit être lié au programme ou à l'unité Pascal via les directives {$L nomdufichier}. Voici des exemples de déclarations de procédure externe :

  1. Procedure MoveWord(Var Source,Dest;Count:Word); External;
  2. Procedure MoveLong(Var Source,Dest;Count:Word); External;
  3. Procedure FillWord(Var Dest;Data:Integer;Count:Word); External;
  4. Procedure FillLong(Var Dest;Data:Longint;Count:Word); External;
  5. {$L BLOCK.OBJ}

Déclarations ASSEMBLER

Avec les déclarations ASSEMBLER, vous pouvez écrire des procédures et des fonctions entières en langage assembleur en ligne :

Déclarations INLINE

La directive INLINE vous permet d'écrire des instructions de code machine à la place d'un bloc de code Pascal.

Lorsqu'une procédure ou une fonction normale est appelée, le compilateur génère du code poussant les paramètres de la procédure ou de la fonction sur la pile, puis génère une instruction CALL pour appeler la procédure ou la fonction. Lorsque vous appelez une procédure ou une fonction INLINE, le compilateur génère du code à partir de la directive en ligne au lieu du CALL. Par conséquent, une procédure ou une fonction INLINE est développée à chaque fois que vous y faites référence, tout comme une macro en langage de programmation assembleur. Voici un court exemple de deux procédures INLINE :

  1. Procedure Disablelnterrupts; Inline($FA); { CLI }
  2. Procedure Enablelnterrupts; Inline($FB); { STI }

Déclarations FUNCTION

Une déclaration de fonction définit un bloc calculant et renvoyant une valeur.

L'entête de la fonction spécifie l'identificateur de la fonction, les paramètres formels (le cas échéant) et le type de résultat de la fonction. Une fonction est activée par l'évaluation d'un appel de fonction. L'appel de fonction donne l'identificateur de la fonction et les paramètres réels, le cas échéant, requis par la fonction. Un appel de fonction apparaît comme un opérande dans une expression. Lorsque l'expression est évaluée, la fonction est exécutée et la valeur de l'opérande devient la valeur renvoyée par la fonction. La partie instruction du bloc de la fonction spécifie les instructions à exécuter lors de l'activation de la fonction. Le bloc doit contenir au moins une instruction d'affectation affectant une valeur à l'identificateur de fonction. Le résultat de la fonction est la dernière valeur attribuée. Si aucune instruction d'affectation n'existe ou si elle n'est pas exécutée, la valeur renvoyée par la fonction est indéfinie. Si l'identificateur de la fonction est utilisé dans un appel de fonction au sein du bloc de la fonction, la fonction est exécutée de manière récursive. Voici des exemples de déclarations de fonction :

  1. Function Max(A:Vector;N:Integer):Extended;
  2. Var
  3.  X:Extended;
  4.  I:Integer;
  5. Begin
  6.  X := A[1];
  7.  For I := 2 to N do If X < A[I] Then X := A[I];
  8.  Max := X;
  9. End;
  10.  
  11. Function Power(X:Extended;Y:Integer):Extended;
  12. Var
  13.  Z:Extended;
  14.  I:Integer;
  15. Begin
  16.  Z := 1.0; 
  17.  I := Y;
  18.  While I > 0 do Begin
  19.   If Odd(I)Then Z := Z * X;
  20.   I := I div 2;
  21.   X := Sqr(X);
  22.  End;
  23.  Power := Z;
  24. End;

Comme les procédures, les fonctions peuvent être déclarées comme NEAR, FAR, FORWARD, EXTERNAL, ASSEMBLER ou INLINE ; mais les fonctions INTERRUPT ne sont pas autorisées.

Déclarations de méthode

La déclaration d'une méthode dans un type d'objet correspond à une déclaration FORWARD de cette méthode. Par conséquent, quelque part après la déclaration de type d'objet et dans la même portée que la déclaration de type d'objet, la méthode doit être implémentée par une déclaration de définition.

Pour les méthodes de procédure et de fonction, la déclaration de définition prend la forme d'une procédure ou d'une fonction normale, mais l'identificateur de procédure ou de fonction est un identificateur de méthode qualifiée. Il s'agit d'un identificateur de type objet suivi d'un point (.) puis d'un identificateur de méthode.

Pour les méthodes constructeur et les méthodes destructrices, la déclaration de définition prend la forme d'une déclaration de méthode de procédure, sauf que le mot réservé PROCEDURE est remplacé par un mot réservé CONSTRUCTOR ou DESTRUCTOR.

Facultativement, la déclaration de définition d'une méthode peut répéter la liste des paramètres formels de l'entête de la méthode dans le type d'objet. Si tel est le cas, l'entête de la méthode de la déclaration de définition doit correspondre exactement à l'ordre, aux types et aux noms des paramètres, ainsi qu'au type du résultat de la fonction, le cas échéant.

Dans la déclaration de définition d'une méthode, il y a toujours un paramètre implicite avec l'identificateur SELF, correspondant à un paramètre de variable formelle possédant le type d'objet. Dans le bloc de méthode, SELF représente l'instance dont la composante de méthode a été désigné pour activer la méthode. Par conséquent, toute modification apportée aux valeurs des champs de SELF est reflétée dans l'instance.

La portée d'un identificateur de composante dans un type d'objet s'étend sur toute procédure, fonction, constructeur ou bloc destructeur implémentant une méthode du type d'objet. L'effet est le même que si le bloc de méthode entier était incorporé dans une instruction WITH dans le format :

With Self do Begin ... End

Pour cette raison, les orthographes des identificateurs de composantes, des paramètres de méthode formels, de SELF et de tous les identificateurs introduits dans une implémentation de méthode doivent être uniques. Voici quelques exemples d'implémentations de méthodes :

  1. Procedure TRectangle.Intersect(Var R:TRectangle);Begin
  2.  if A.X < R.A.X then A.X := R.A.X;
  3.  if A.Y < R.A.Y then A.Y := R.A.Y;
  4.  if B.X > R.B.X then B.X := R.B.X;
  5.  if B.Y > R.B.Y then B.Y := R.B.Y;
  6.  if (A.X >= B.X) or (A.Y >= B.Y) then Init(0, 0, 0, 0);
  7. End;
  8.  
  9. Procedure TField.Display;Begin
  10.  GotoXY(X,Y);
  11.  Write(Name^, ' ',GetStr);
  12. End;
  13.  
  14. Function TNumField.PutStr(S:String):Boolean;
  15. Var
  16.  E:Integer;
  17. Begin
  18.  Val(S, Value, E);
  19.  PutStr := (E = 0) and (Value >= Min) and (Value <= Max);
  20. End;

Constructeurs et destructeurs

Les constructeurs et les destructeurs sont des formes spécialisées de méthodes. Utilisés en relation avec la syntaxe étendue des procédures standard New et Dispose, les constructeurs et les destructeurs ont la capacité d'allouer et de désallouer des objets dynamiques. De plus, les constructeurs ont la possibilité d'effectuer l'initialisation requise des objets contenant des méthodes virtuelles. Comme d'autres méthodes, les constructeurs et les destructeurs peuvent être hérités, et un objet peut avoir n'importe quel nombre de constructeurs et de destructeurs.

Les constructeurs sont utilisés pour initialiser les objets nouvellement créés. Généralement, l'initialisation est basée sur des valeurs passées en paramètres au constructeur. Les constructeurs ne peuvent pas être virtuels car le mécanisme de répartition de la méthode virtuelle dépend du fait qu'un constructeur a d'abord initialisé l'objet.

Voici quelques exemples de constructeurs :

  1. Constructor TField.CopyTo(Var F:TField);Begin
  2.  Self:=F;
  3. End;
  4.  
  5. Constructor TField.Init(FX,FY,FLen:Integer;FName:String);Begin
  6.  X:=FX;
  7.  Y:=FY;
  8.  Len:=FLen;
  9.  GetMem(Name,Length(FName)+1);
  10.  Name^:=FName;
  11. End;
  12.  
  13. Constructor TStrField.Init(FX,FY,FLen:Integer; FName:String);Begin
  14.  Inherited Init(FX,FY,FLen,FName);
  15.  GetMem(Value, Len);
  16.  Value^ := '';
  17. End;

La première action d'un constructeur d'un type descendant, tel que le TStrField.Init précédent, est presque toujours d'appeler le constructeur correspondant de son ancêtre immédiat pour initialiser les champs hérités de l'objet. Ceci fait, le constructeur initialise alors les champs de l'objet ayant été introduits dans le descendant.

Les destructeurs sont les homologues des constructeurs et sont utilisés pour nettoyer les objets après leur utilisation. En règle générale, le nettoyage consiste à supprimer tous les champs de pointeur alloués par l'objet.

Voici quelques exemples de destructeurs :

  1. Destructor TField.Done;Begin
  2.  FreeMem(Name,Length(Name^)+1);
  3. End;
  4.  
  5. Destructor TStrField.Done;Begin
  6.  FreeMem(Value,Len);
  7.  Inherited Done;
  8. End;

Un destructeur d'un type descendant, tel que le précédent TStrField.Done, supprime généralement les champs de pointeur introduits dans le descendant, puis, comme dernière action, appelle le destructeur correspondant de son ancêtre immédiat pour supprimer tout champ de pointeur hérité de l'objet.

Le Turbo Pascal vous permet d'installer une fonction d'erreur de tas via la variable HeapError dans l'unité System. Cette fonctionnalité affecte la façon dont les constructeurs de type objet fonctionnent.

Par défaut, lorsqu'il n'y a pas assez de mémoire pour allouer une instance dynamique d'un type d'objet, un appel de constructeur utilisant la syntaxe étendue de la procédure standard New génère une erreur d'exécution 203. Si vous installez une fonction HeapError renvoyant 1 plutôt que le résultat de fonction standard de 0, un appel de constructeur via New renverra NIL lorsqu'il ne peut pas terminer la requête (au lieu d'abandonner le programme).

Le code effectuant l'initialisation du champ d'allocation et de table de méthode virtuelle (VMT) d'une instance dynamique fait partie de la séquence d'entrée d'un constructeur :

lorsque le contrôle arrive au BEGIN de la partie instruction du constructeur, l'instance a déjà été allouée et initialisée. Si l'allocation échoue et que la fonction d'erreur de tas renvoie 1, le constructeur ignore l'exécution de la partie instruction et renvoie un pointeur NIL. Le pointeur spécifié dans la construction New ayant appelé le constructeur est défini sur NIL.

Une fois que le contrôle arrive au BEGIN de la partie instruction d'un constructeur, l'instance de type objet est garantie d'avoir été allouée et initialisée avec succès. Le constructeur lui-même, cependant, peut tenter d'allouer des variables dynamiques pour initialiser les champs de pointeur dans l'instance et, à son tour, ces allocations peuvent échouer. Si cela se produit, un constructeur bien élevé devrait inverser toutes les allocations réussies et désallouer l'instance de type objet afin que le résultat net devienne un pointeur NIL. Pour rendre une telle sauvegarde possible, le Turbo Pascal implémente la procédure standard Fail ne prenant aucun paramètre et ne peut être appelée qu'à partir d'un constructeur. Un appel à Fail amène un constructeur à désallouer l'instance dynamique ayant été allouée à l'entrée du constructeur et provoque le retour d'un pointeur NIL pour indiquer son échec.

Lorsque des instances dynamiques sont allouées via la syntaxe étendue de New, une valeur résultante de NIL dans la variable de pointeur spécifiée indique que l'opération a échoué. Malheureusement, il n'y a pas de telle variable de pointeur à inspecter après la construction d'une instance statique ou lorsqu'un constructeur hérité est appelé. Au lieu de cela, le Turbo Pascal permet à un constructeur d'être utilisé comme une fonction booléenne dans une expression : une valeur de retour de True indique le succès, et une valeur de retour de False indique un échec dû à un appel à Fail dans le constructeur.

Sur le disque du logiciel Turbo Pascal, vous trouverez deux programmes, NORECVER.PAS et RECOVER.PAS. Les deux implémentent deux types d'objets simples contenant des pointeurs. La version NORECVER du programme n'implémente pas la récupération d'erreur de constructeur.

Le RECOVER.PAS montre comment le programme peut être réécrit pour implémenter la récupération d'erreur. Remarquez comment les destructeurs correspondants dans Base.Init et Derived.Init sont utilisés pour inverser toutes les allocations réussies avant que Fail ne soit appelé pour finalement échouer l'opération. Notez également que dans Derived.Init, l'appel à Base.Init est codé dans une expression afin que le succès du constructeur hérité puisse être testé.

Paramètres

La déclaration d'une procédure ou d'une fonction spécifie une liste de paramètres formels. Chaque paramètre déclaré dans une liste de paramètres formelle est local à la procédure ou à la fonction déclarée. Votre programme peut y faire référence par son identificateur dans le bloc associé à la procédure ou à la fonction.

Il existe quatre types de paramètres : valeur, constante, variable et non typé. Ceux-ci se caractérisent comme suit :

Paramètres de valeur

Un paramètre de valeur formelle agit comme une variable locale à la procédure ou à la fonction, sauf qu'il obtient sa valeur initiale à partir du paramètre actuel correspondant lors de l'activation de la procédure ou de la fonction. Les modifications apportées à un paramètre de valeur formelle n'affectent pas la valeur du paramètre actuel.

Le paramètre actuel correspondant d'un paramètre de valeur dans une instruction de procédure ou un appel de fonction doit être une expression, et sa valeur ne doit pas être de type fichier ou de tout type structuré contenant un type de fichier.

Le paramètre actuel doit être compatible avec l'affectation avec le type du paramètre de valeur formelle. Si le type de paramètre est chaîne de caractères, le paramètre formel reçoit un attribut de taille de 255.

Paramètres constants

Un paramètre constant formel agit comme une variable locale en lecture seulement obtenant sa valeur à partir du paramètre actuel correspondant lors de l'activation de la procédure ou de la fonction. Les affectations à un paramètre constant formel ne sont pas autorisées. De même, un paramètre constant formel ne peut pas être passé en tant que paramètre variable actuel à une autre procédure ou fonction.

Le paramètre actuel correspondant d'un paramètre constant dans une instruction de procédure ou une fonction doit suivre les mêmes règles qu'un paramètre de valeur actuel.

Dans les cas où un paramètre formel ne change jamais sa valeur pendant l'exécution d'une procédure ou d'une fonction, un paramètre constant doit être utilisé à la place d'un paramètre de valeur. Les paramètres constants permettent à l'implémenteur d'une procédure ou d'une fonction de se protéger contre les affectations accidentelles à un paramètre formel. De plus, pour les paramètres de type structuré et chaîne de caractères, le compilateur peut générer un code plus efficace lorsque des paramètres constants sont utilisés au lieu de paramètres de valeur.

Paramètres variables

Un paramètre variable est utilisé lorsqu'une valeur doit être transmise d'une procédure ou d'une fonction à l'appelant. Le paramètre actuel correspondant dans une instruction de procédure ou un appel de fonction doit être une référence de variable. Le paramètre de variable formelle représente la variable actuelle lors de l'activation de la procédure ou de la fonction, de sorte que toute modification de la valeur du paramètre de variable formelle est reflétée dans le paramètre actuelle.

Dans la procédure ou la fonction, toute référence au paramètre de variable formelle accède au paramètre actuel lui-même. Le type du paramètre actuel doit être identique au type du paramètre de la variable formelle (vous pouvez contourner cette restriction via des paramètres non typés).

La directive du compilateur $P contrôle la signification d'un paramètre variable déclaré à l'aide du mot-clef STRING. Dans l'état par défaut {$P-}, STRING correspond à un type de chaîne de caractères avec un attribut de taille de 255. Dans l'état {$P+}, STRING indique que le paramètre est un paramètre de chaîne de caractères ouverte.

Si le référencement d'un paramètre de variable actuelle implique l'indexation d'un tableau ou la recherche de l'objet d'un pointeur, ces actions sont exécutées avant l'activation de la procédure ou de la fonction.

Les règles de compatibilité d'affectation de type objet s'appliquent également aux paramètres variables de type objet : pour un paramètre formel de type T1, le paramètre actuel peut être de type T2 si T2 est dans le domaine de T1.

Paramètres non typés

Lorsqu'un paramètre formel est un paramètre non typé, le paramètre actuel correspondant peut être n'importe quelle référence variable ou constante, quel que soit son type. Un paramètre non typé déclaré à l'aide du mot clef VAR peut être modifié, alors qu'un paramètre non typé déclaré à l'aide du mot clef CONST est en lecture seulement.

Dans la procédure ou la fonction, le paramètre non typé est sans type ; c'est-à-dire qu'il est incompatible avec les variables de tous les autres types, à moins qu'il ne reçoive un type spécifique via un castre de variable. Voici un exemple de paramètres non typés :

  1. Function Equal(Var Source,Dest;Size:Word):Boolean;
  2. Type
  3.  TBytes = array[0..65534] of Byte;
  4. Var
  5.  N:Word;
  6. Begin
  7.  N := 0;
  8.  while(N < Size) and (TBytes(Dest) [N] = TBytes(Source) [N]) do Inc(N);
  9.  Equal:=N=Size;
  10. End;

Cette fonction peut être utilisée pour comparer deux variables de n'importe quelle taille. Par exemple, étant donné les déclarations :

  1. Type
  2.  TVector=Array[1..10] of Integer;
  3.  TPoint=Record
  4.   X,Y:Integer;
  5.  End;
  6.  
  7. Var
  8.  Vecl,Vec2:TVector;
  9.  N:Integer;
  10.  P:TPoint;

la fonction appelle alors :

  1. Equal(Vec1, Vec2, SizeOf(TVector))
  2. Equal(Vec1, Vec2, SizeOf(Integer) * N)
  3. Equal(Vec[l] , Vec1[6], SizeOf(Integer) * 5)
  4. Equal(Vec1[l] , P, 4)

comparant Vec1 à Vec2, les N premières composantes de Vec1 aux N premières composantes de Vec2, les cinq premières composantes de Vec1 aux cinq dernières composantes de Vec1, et Vec1[1] à P.X et Vec1[2] à P.Y.

Bien que les paramètres non typés vous offrent une plus grande flexibilité, ils peuvent être plus risqués à utiliser. Le compilateur ne peut pas vérifier que les opérations sur les variables non typées sont valides.

Paramètres ouverts

Les paramètres ouverts permettent de transmettre des chaînes de caractères et des tableaux de tailles différentes à la même procédure ou fonction. Les paramètres de chaîne de caractères ouverte peuvent être déclarés de deux manières :

L'identificateur OpenString est déclaré dans l'unité System. Il désigne un type de chaîne de caractères spécial ne pouvant être utilisé que dans la déclaration de paramètres de chaîne de caractères. Pour des raisons de compatibilité descendante, OpenString n'est pas un mot réservé, cela signifie qu'OpenString peut être redéclaré en tant qu'identificateur défini par l'utilisateur.

Lorsque la compatibilité descendante n'est pas un problème, une directive de compilateur {$P+} peut être utilisée pour modifier la signification du mot clef STRING. Dans l'état {$P+}, une variable déclarée à l'aide du mot clef STRING est un paramètre de chaîne de caractères ouverte.

Pour un paramètre de chaîne de caractères ouverte, le paramètre actuel peut être une variable de n'importe quel type de chaîne de caractères. Dans la procédure ou la fonction, l'attribut de taille (longueur maximale) du paramètre formel sera le même que celui du paramètre actuel.

Les paramètres de chaîne de caractères ouverte se comportent exactement comme des paramètres variables d'un type chaîne de caractères, sauf qu'ils ne peuvent pas être transmis en tant que paramètres variables normaux à d'autres procédures et fonctions. Ils peuvent cependant être à nouveau transmis en tant que paramètres de chaîne de caractères ouverte. Dans cet exemple, le paramètre S de la procédure AssignStr est un paramètre de chaîne de caractères ouverte :

  1. Procedure AssignStr(var S:OpenString);Begin
  2.  S:='0123456789ABCDEF';
  3. End;

Étant donné que S est un paramètre de chaîne de caractères ouverte, les variables de n'importe quel type de chaîne de caractères peuvent être transmises à AssignStr :

  1. Var
  2.  S1:String[10];
  3.  S2:String[20];
  4. Begin
  5.  AssignStr(81);
  6.  AssignStr(82);
  7. End;

Dans AssignStr, la longueur maximale du paramètre S est la même que celle du paramètre actuel. Par conséquent, lors du premier appel à AssignStr, l'affectation au paramètre S tronque la chaîne de caractères car la longueur maximale déclarée de S1 est de 10.

Lorsqu'elle est appliquée à un paramètre de chaîne de caractères ouverte, la fonction standard bas renvoie zéro, la fonction standard haut renvoie la longueur maximale déclarée du paramètre actuel et la fonction SizeOf renvoie la taille du paramètre actuel.

Dans l'exemple suivant, la procédure FillString remplit une chaîne de caractères jusqu'à sa longueur maximale avec un caractère donné. Notez l'utilisation de la fonction High standard pour obtenir la longueur maximale d'un paramètre de chaîne de caractères ouverte.

  1. Procedure FillString(Var S:OpenString; Ch:Char);Begin
  2.  S[0] := Chr(High(S));        { Définit la longueur de la chaîne de caractères }
  3.  FillChar(S[1], High(8), Ch); { Définit la chaîne de caractères } 
  4. End;

Les paramètres de valeur et de constante déclarés à l'aide de l'identificateur OpenString ou du mot clef de chaîne de caractères dans l'état {$P+} ne sont pas des paramètres de chaîne de caractères ouverte. Au lieu de cela, ces paramètres se comportent comme s'ils étaient déclarés à l'aide d'un type de chaîne de caractères d'une longueur maximale de 255, et la fonction High standard renvoie toujours 255 pour ces paramètres. Lorsque les paramètres ouverts sont activés (à l'aide d'une directive de compilateur {$P+}), un paramètre formel est déclaré à l'aide de la syntaxe suivante :

  1. Array of T

est un paramètre de tableau ouvert. T doit être un identificateur de type et le paramètre actuel doit être une variable de type T ou une variable de tableau dont le type d'élément est T. Dans la procédure ou la fonction, le paramètre formel se comporte comme s'il était déclaré comme :

  1. Array[0..N-1] of T

N est le nombre d'éléments dans le paramètre actuel. En effet, l'intervalle d'index du paramètre actuel est cartographiée sur les entiers à N-1. Si le paramètre actuel est une variable simple de type T, il est traité comme un tableau avec un élément de type T.

Un paramètre de tableau ouvert formel est accessible par élément uniquement. Les affectations à un tableau ouvert entier ne sont pas autorisées et un tableau ouvert peut être transmis à d'autres procédures et fonctions uniquement en tant que paramètre de tableau ouvert ou en tant que paramètre de variable non typé.

Les paramètres de tableau ouvert peuvent être des paramètres de valeur, constants et variables et ont la même sémantique que les paramètres de valeur, constants et variables normaux. En particulier, les affectations aux éléments d'un paramètre constant de tableau ouvert formel ne sont pas autorisées, et les affectations aux éléments d'un paramètre de valeur de tableau ouvert formel n'affectent pas le paramètre actuel.

Pour un paramètre de valeur de tableau ouvert, le compilateur crée une copie locale du paramètre actuel dans le cadre de pile de la procédure ou de la fonction. Par conséquent, veillez à ne pas déborder la pile lorsque vous passez de grands tableaux en tant que paramètres de valeur de tableau ouvert.

Lorsqu'elle est appliquée à un paramètre de tableau ouvert, la fonction standard bas renvoie zéro, la fonction standard haut renvoie l'index du dernier élément dans le paramètre de tableau actuel et la fonction SizeOf renvoie la taille du paramètre de tableau actuel.

La procédure Clear de l'exemple suivant affecte zéro à chaque élément d'un tableau de Real et la fonction Sum calcule la somme de tous les éléments d'un tableau de Real. Étant donné que le paramètre A dans les deux cas est un paramètre de tableau ouvert, les sous-programmes peuvent fonctionner sur n'importe quel tableau avec un type d'élément de Real.

  1. Procedure Clear(Var A:Array of Real);
  2. Var
  3.  I:Word;
  4. Begin
  5.  For I:=0 to High(A) do A[I]:=0;
  6. End;
  7.  
  8. Function Sum(Const A:Array of Real):Real;
  9. Var
  10.  I:Word;
  11.  S:Real;
  12. Begin
  13.  S:=0;
  14.  For I:=0 to High(A) do S:=S+A[I];
  15.  Sum:=S;
  16. End;

Lorsque le type d'élément d'un paramètre de tableau ouvert est Char, le paramètre actuel peut être une constante de chaîne de caractères. Par exemple, étant donné la déclaration de la procédure suivante :

  1. Procedure PrintStr(Const S:Array of Char);
  2. Var
  3.  I:Integer;
  4. Begin
  5.  For I:=0 to High(S) do If S[I] <> #0 Then Write(S[I]) Else Break;
  6. End;

les instructions de procédure suivantes sont valides :

  1. PrintStr('Bonjour Gladir.com');
  2. PrintStr('A');

Lorsqu'elle est passée en tant que tableau de caractères ouverts, une chaîne de caractères vide est convertie en une chaîne de caractères avec un élément contenant un caractère NULL, de sorte que l'instruction PrintStr() est identique à l'instruction PrintStr(#0).

Variables dynamiques de type objet

Les procédures standard New et Dispose autorisent un appel de constructeur ou de destructeur comme deuxième paramètre pour l'allocation ou la suppression d'une variable de type objet dynamique. C'est la syntaxe :

  1. New(P, Construct)

et

  1. Dispose(P, Destruct)

P est une variable pointeur, pointant vers un type d'objet, et Construct et Destruct sont des appels aux constructeurs et aux destructeurs de ce type d'objet. Pour New, l'effet de la syntaxe étendue est le même que l'exécution :

  1. New(P);
  2. P^.Construct;

Et pour Dispose, l'effet de la syntaxe étendue est le même que l'exécution :

  1. P^.Destruct;
  2. Dispose(P);

Sans la syntaxe étendue, vous devriez souvent appeler New suivi d'un appel de constructeur ou appeler un destructeur suivi d'un appel à Dispose. La syntaxe étendue améliore la lisibilité et génère un code plus court et plus efficace. Ce qui suit illustre l'utilisation de la syntaxe étendue New et Dispose :

  1. Var
  2.  SP:PStrField;
  3.  ZP:PZipField;
  4. Begin
  5.  New(SP, Init(l, 1, 25, 'Prenom'));
  6.  New(ZP, Init (1, 2, 5, 'Code postal', 0, 99999));
  7.  SP^.Edit;
  8.  ZP^.Edit;
  9.   { : }
  10.  Dispose(ZP, Done);
  11.  Dispose(SP, Done);
  12. End;

Vous pouvez également utiliser New comme fonction allouant et renvoyant une variable dynamique d'un type spécifié :

  1. New(T)

ou

  1. New(T, Construct)

Dans la première forme, T peut être n'importe quel type de pointeur. Dans la seconde forme, T doit pointer vers un type d'objet et Construct doit être un appel à un constructeur de ce type d'objet. Dans les deux cas, le type du résultat de la fonction est T. Voici un exemple :

  1. Var
  2.  F1,F2:PField;
  3. Begin
  4.  F1:=New(PStrField, Init(1, 1, 25, 'Prenom');
  5.  F2:=New(PZipField, Init(1, 2, 5, 'Code postal', 0, 99999));
  6.   { : }
  7.  WriteLn(F1^.GetStr);    { Appels TStrField.GetStr }
  8.  WriteLn(F2^.GetStr);    { Appels TZipField.GetStr }
  9.   { : } 
  10.  Dispose(F2,Done);       { Appels TField.Done }
  11.  Dispose(F1,Done);       { Appels TStrField.Done }
  12. End;

Notez que même si F1 et F2 sont de type PField, les règles de compatibilité d'affectation de pointeur étendu permettent d'affecter à F1 et F2 un pointeur vers n'importe quel descendant de TField. Étant donné que GetStr et Done sont des méthodes virtuelles, le mécanisme de répartition de la méthode virtuelle appelle correctement TStrField.GetStr, TZipField.GetStr, TField.Done et TStrField.Done, respectivement.



Dernière mise à jour : Samedi, le 1er janvier 2022