Programmation Pascal/Version imprimable

Un livre de Wikilivres.
Aller à : navigation, rechercher
Nuvola-inspired File Icons for MediaWiki-fileicon-ps.png

Ceci est la version imprimable de Programmation Pascal.

  • Si vous imprimez cette page, choisissez « Aperçu avant impression » dans votre navigateur, ou cliquez sur le lien Version imprimable dans la boîte à outils, vous verrez cette page sans ce message, ni éléments de navigation sur la gauche ou en haut.
  • Cliquez sur Rafraîchir cette page pour obtenir la dernière version du wikilivre.
  • Pour plus d'informations sur les version imprimables, y compris la manière d'obtenir une version PDF, vous pouvez lire l'article Versions imprimables.


Programmation Pascal

Une version à jour et éditable de ce livre est disponible sur Wikilivres,
une bibliothèque de livres pédagogiques, à l'URL :
https://fr.wikibooks.org/wiki/Programmation_Pascal

Vous avez la permission de copier, distribuer et/ou modifier ce document selon les termes de la Licence de documentation libre GNU, version 1.2 ou plus récente publiée par la Free Software Foundation ; sans sections inaltérables, sans texte de première page de couverture et sans Texte de dernière page de couverture. Une copie de cette licence est incluse dans l'annexe nommée « Licence de documentation libre GNU ».

Introduction

Wikipedia-logo-v2.svg

Wikipédia propose un article sur : « Pascal (langage) ».

Le langage de programmation Pascal (dont le nom vient du mathématicien français Blaise Pascal) a été inventé par Niklaus Wirth dans les années 1970. Il a été conçu pour servir à l'enseignement de la programmation de manière rigoureuse mais simple, en réaction à la complexité de l'ALGOL 68. Le Pascal est donc principalement utilisé dans l'enseignement et notamment dans les universités.

Ce livre explique comment implémenter un algorithme impératif en Pascal pour ensuite l'exécuter sur une machine. Ce livre n'enseigne pas comment créer des algorithmes et résoudre des problèmes mais comment, à partir d'un algorithme impératif créer un programme informatique.

Prérequis cognitif :

  • Algorithmique impérative, nécessaire pour créer des algorithmes avant de les implémenter.
  • Utilisation simple de l'ordinateur (édition de texte, shell simple)

Prérequis matériels :

  • un ordinateur, n'importe lequel et de n'importe qu'elle architecture du moment que les prérequis logiciels sont respectés.

Prérequis logiciels :

  • un éditeur de texte : le plus simple suffit. Un Environnement de développement pascal serait un plus.
  • un compilateur Pascal fonctionnant pour cet ordinateur.

Exemple d'environnement de travail classique :


Un premier programme

Le fichier source[modifier | modifier le wikicode]

Le classique Hello world en Pascal, enregistrez le code suivant dans un fichier texte nommé hworld.pas :

program hworld;
begin
    writeln("Hello world!")
end.

Compilez ce programme, et lancez-le. Le message suivant doit normalement s'afficher :

Hello world!

Le code source en détail[modifier | modifier le wikicode]

 program hworld;

Le code source commence par déclarer qu'il s'agit d'un programme nommé hworld

Remarque:

Le nom du programme pourrait être Hello_World et comporter ici 11 caractères par exemple, même sous DOS: la chaine de caractères n'est pas trop longue car il ne s'agit pas là du nom du fichier DOS en format 8.3, mais simplement du nom du programme pour le compilateur.
Par contre, le fichier quand à lui devra ne comporter que 8 caracrètres, par exemple hwold.pas.


 begin

Début de la routine principale, appelée au démarrage du programme.


     writeln("Hello world!")

Appel à la procédure writeln (write line) pour afficher la chaîne "Hello world!" sur la sortie standard.


 end.

Fin de la routine principale et du programme.

Donc,

 program Hello_World;
 begin
    writeln('Hello world!')
 end.

se compile et s'exécute très bien avec n'importe quel compilateur Pascal, y compris les anciens depuis Turbo Pascal 3 sous DOS.


Structure d'un programme

Instructions et blocs[modifier | modifier le wikicode]

Une instruction séquence d'instructions regroupe plusieurs instructions en une seule. Celle-ci commence par le mot clé begin et se termine par end. Le ; sert à séparer deux instructions. Voici un exemple :

  begin
    temp := x  ;
    x := y     ;
    y := temp
  end;

Cette instruction Pascal sera traduite par un bloc en Java

  {
    int temp = x ;
    x = y    ;
    y = temp ;
  }

Catégorie[modifier | modifier le wikicode]

Un fichier source Pascal débute toujours par un mot clé indiquant le type de code :

  • Un programme, c'est à dire une application indépendante (program) ;
  • Un module utilisable dans un programme ou un autre module (unit).

Ce mot clé doit être suivi d'un identifiant (un nom) afin de pouvoir faire référence à ce module ou ce programme.

Afin que le compilateur trouve les modules à partir de leur nom, il faut que le nom du fichier source soit identique : un module nommé par :

 unit my_unit;

doit être enregistré dans un fichier nommé my_unit.pas.

Exemple 1 : un programme[modifier | modifier le wikicode]

 program first_app;
 ...

Exemple 2 : un module[modifier | modifier le wikicode]

 unit my_unit;
 ...

Exemple 3 : un programme utilisant le module de l'exemple 2[modifier | modifier le wikicode]

Le mot clé uses spécifie la liste des modules utilisés :

 program second_app;
 uses my_unit;
 ...

Compilation[modifier | modifier le wikicode]

L'extension des fichiers produits par la compilation varie selon la plateforme utilisée et le compilateur (natif ou P-code).

Cependant, la plupart des compilateurs ne gère pas la compilation en chaîne : chaque module et programme doit être compilé séparemment et deux modules ne peuvent donc s'utiliser mutuellement.

Commentaires[modifier | modifier le wikicode]

Les commentaires sont encadrés par les accolades :

 program first_app;
 { ma première application en Pascal }
 ...

Alternativement, on peut utiliser les parenthèses-étoiles :

 program first_app;
 (* ma première application en Pascal *)
 ...

Ce qui était plus pratique avec les anciens claviers ne comportant pas les accolades.

Le compilateur Turbo Pascal utilise certains commentaires spéciaux pour préciser des directives de compilations. Ceux-ci débutent par un caractère $, par exemple {$R-,I-} pour désactiver la vérification des erreurs d'intervalle (Range errors) et des erreurs d'entrée-sortie (I/O errors).

Structure générale d'un programme[modifier | modifier le wikicode]

 program {nom du programme};
 
 uses {liste des modules utilisés};

 {...déclaration des variables globales et des types...}
 {...déclaration des procédures et fonctions...}
 
 begin
     {...instructions de la routine principale...}
 end.

Structure générale d'un module[modifier | modifier le wikicode]

Le module est divisé en deux grandes parties :

  • La partie interface expose les fonctions, procédures, variables et types publiques, c'est à dire accessibles depuis les modules et programmes qui utilisent ce module.
  • La partie implementation contient le code implémentant les fonctions et procédures publiques, et peut contenir d'autres variables, types, fonctions et procédures internes au module.
 unit {nom du module};
 
 interface
 
 uses {liste des modules utilisés};

 {...déclaration des variables globales et des types...}
 {...déclaration des procédures et fonctions...}
 
 implementation
 
 uses {liste des modules utilisés};

 {...déclaration des variables globales et des types...}
 {...déclaration des procédures et fonctions...}
 
 begin
     {...instructions d'initialisation du module...}
 end.


Variables

Une variable sert à stocker une donnée. Elle a un nom, et un type (entier, chaîne de caractères, tableaux d'entiers, ...).

Déclaration des variables[modifier | modifier le wikicode]

Les variables peuvent être déclarées en début de programme ou de module. Dans ce cas, elles sont globales, car accessibles à toutes les procédures et fonctions qui suivent.

Les variables peuvent également être déclarées en début de procédure ou de fonction, où elle seront locales à celle-ci.

Les déclarations de variables commencent par le mot clé var, puis chaque variable est déclarée selon la syntaxe suivante :

 nom_variable : type_de_variable ;

Il est également possible de regrouper les variables ayant le même type :

 nom_variable1 , nom_variable2 : type_de_variable ;

Par exemple :

 var
     i,j : integer;
     s : String;

Affectation[modifier | modifier le wikicode]

Pour affecter une valeur constante ou le résultat d'une expression à une variable, il faut utiliser l'opérateur := (caractère deux-points suivi du caractère égal).

Par exemple :

 i:=5;
 j:=i+10;

Affectation à la déclaration[modifier | modifier le wikicode]

Il est possible d'affecter une valeur à la déclaration d'une variable, en utilisant l'opérateur égal ( = ), le caractère deux-points étant déjà utilisé pour déclarer le type :

 var i:integer=5;


Procédures et fonctions

Une procédure ou une fonction est une sous-routine, en général appelée plusieurs fois. Ceci permet de regrouper un code utilisé plusieurs fois, de diviser le programme en sous-programmes.

Différences procédure/fonction[modifier | modifier le wikicode]

Procédures et fonctions acceptent des paramètres, seule une fonction retourne une valeur. La procédure n'en retourne pas.

Déclaration d'une procédure[modifier | modifier le wikicode]

La déclaration d'une procédure se fait avec le mot clé procedure suivi du nom de la procédure, et de la liste des paramètres entre parenthèses. Cette liste de paramètres est déclarée avec la même syntaxe que les variables.

Ensuite, les variables locales sont déclarées, puis les instructions sont comprises dans un bloc begin ... end;

Par exemple :

 procedure NumeroFoisDix( numero : integer ) ;
 var j : integer;
 begin
     j := numero * 10 ;
     println('j = ', j) ;
 end ;

Déclaration d'une fonction[modifier | modifier le wikicode]

La syntaxe est similaire à celle d'une procédure, excepté que le mot clé utilisé est function et que le type de retour doit être déclaré.

Par exemple :

 function NumeroPlusCinq( numero : integer ) : integer ;
 var j : integer ;
 begin
     j := numero + 5 ;
     NumeroPlusCinq := j ;
 end ;

Retourner une valeur se fait en affectant celle-ci au nom de la fonction.

Interface et implémentation dans un module[modifier | modifier le wikicode]

Dans un module, la partie implentation contient le code des sous-routines en utilisant la syntaxe des paragraphes précédant. La partie interface ne reprend que la première ligne.

Par exemple :

 unit MonModule;
 
 interface
 
 procedure NumeroFoisDix( numero : integer ) ;
 function NumeroPlusCinq( numero : integer ) : integer ;
 
 implementation
 
 procedure NumeroFoisDix( numero : integer ) ;
 var j : integer;
 begin
     j := numero * 10 ;
     println("j = ", j) ;
 end ;
 
 function NumeroPlusCinq( numero : integer ) : integer ;
 var j : integer ;
 begin
     j := numero + 5 ;
     NumeroPlusCinq := j ;
 end 
 
 end.

Ordre de déclaration[modifier | modifier le wikicode]

La compilation en une passe implique que chaque fonction ou procédure appelée doit être définie avant. L'ordre des sous-routines est donc important.

L'exemple suivant produit une erreur de compilation :

 procedure FaireFaire;
 begin
     println('Procédure qui sous-traite l''action');
     Faire;
 end;
 
 procedure Faire;
 begin
     println('Procédure qui fait l''action');
 end;

L'ordre correct est le suivant :

 procedure Faire;
 begin
     println('Procédure qui fait l''action');
 end;
 
 procedure FaireFaire;
 begin
     println('Procédure qui sous-traite l''action');
     Faire;
 end;

Cette restriction ne s'applique pas aux sous-routines publiques d'un même module, car elles sont pré-déclarées dans la partie interface.

Il existe des cas où deux procédures s'appellent mutuellement. Aucune ne peut être située avant/après l'autre. Dans ce cas, il faut en prédéclarer une avec le mot clé forward.

Par exemple :

 procedure GrandCalcul(n:integer); forward;
 
 procedure PetitCalcul(n:integer);
 begin
   if n>100 then GrandCalcul(n);
   else begin
     ...
   endif;
 end;
 
 procedure GrandCalcul(n:integer);
 begin
   if n<=100 then PetitCalcul(n);
   else begin
     ...
   endif;
 end;


Dépendances mutuelles et références circulaires

Dépendances mutuelles et références circulaires[modifier | modifier le wikicode]

Parfois, on est amené à faire deux unités où chacune fait références aux fonctions de l'autre. Il s'agit d'une dépendance mutuelle. Dans certains cas, cela peut poser des difficultés.

Dépendance au niveau implémentation[modifier | modifier le wikicode]

Pour commencer, il y a un cas où cela marche sans problème, c'est quand la clause uses qui fait référence à l'autre unité se trouve dans la section d'implémentation. Dans ce cas, l'ensemble des déclarations de types, de procédures et de fonctions sont lues dans chacune des deux unités. En effet, ces déclaration se trouve dans la section interface, c'est-à-dire avant que la question de la dépendance mutuelle soit soulevée. Quand, dans l'implémentation, on fait référence à l'autre unité, tous les membres publics sont déjà définis.

Dépendance mixte interface-implémentation[modifier | modifier le wikicode]

Il se peut qu'une unité A fasse référence à une unité B par une cause uses dans sa partie implémentation, tandis que l'unité B fasse référence l'unité A dans sa partie interface. Dans ce cas, le compilateur va d'abord lire l'interface de A parce qu'elle est nécessaire à l'interprétation de l'interface de l'unité B. Par exemple, l'unité B peut proposer des fonctions qui prennent en paramètre un type défini dans l'unité A. Une fois l'interface de B déterminée, le compilateur va s'occuper des implémentations. Par la suite, l'unité A et l'unité B peuvent utiliser dans leur implémentation les fonctions de l'autre. Référence circulaire ou dépendance interface-interface

Maintenant, si l'on fait deux unités qui font référence l'une à l'autre, mais avec une clause uses dans leur interface, on obtient l'erreur de référence circulaire. En effet, pour pouvoir interprêter correctement l'interface d'une unité, il faut que le compilateur ait lu l'interface de l'autre unité, et vice-versa. Certains compilateurs ne génère pas d'erreur dans ces cas-là, mais en Pascal, pour le moment, une telle chose est impossible.

Fausse référence circulaire et vraie référence circulaire[modifier | modifier le wikicode]

Il se peut qu'en réalité, il ne soit pas nécessaire de déclarer la dépendance au niveau de l'interface de l'unité. Dans ce cas, il suffit de déplacer une partie de la clause uses dans la partie implémentation, pour les unités ne faisant appel seulement à ce moment à des procédures, des fonctions, ou à des types déclarés dans d'autres unités. C'est le cas le plus fréquent fort heureusement.

Sinon, c'est généralement que certains objets définis dans une unité A contiennent des champs ou des méthodes utilisant des objets définis dans une unité B, qui eux-même contiennent des champs ou des méthodes utilisant des objets définis dans l'unité A. Par exemple :

{ dans l'unité A }
type
  TObjetA = class
    function DonneObjetB: TObjetB;
  end;
{ dans l'unité B }
type
  TObjetB = class
    function DonneObjetA: TObjetA;
  end;
Solution par la fusion[modifier | modifier le wikicode]

On peut régler le problème de la référence circulaire en mettant les deux objets dans une seule et même unité et en prédéclarant les types. Cela se fait de la manière suivante :

{ dans l'unité fusionnée AB }
type
  TObjetA = class; { prédéclaration de A }
  TObjetB = class; { prédéclaration de B }

  TObjetA = class  { déclaration complète de A }
    function DonneObjetB: TObjetB;
  end;
  TObjetB = class  { déclaration complète de B }
    function DonneObjetA: TObjetA;
  end;

Bien entendu, il se peut que, de fil en aiguille, on obtiennent des fichiers de code très gros, ce qui est l'inconvénient de cette méthode.

Solution par le non-typage[modifier | modifier le wikicode]

Enfin, on peut résoudre le problème en ne typant pas les champs ou les paramètres et les valeurs de retour des procédures et des fonctions.

{ dans l'unité A }
type
  TObjetA = class
    function DonneObjetB: TObject;
  end;
{ dans l'unité B }
type
  TObjetB = class
    function DonneObjetA: TObject;
  end;

Bien que la solution ne soit pas très élégante, elle permet de contourner le problème. Lors de l'appel des fonctions DonneObjetA et DonneObjetB, il faudra faire un transtypage pour avoir un objet bel et bien identifié comme étant de type TObjetA ou TObjetB. Par exemple :

procedure UtiliseObjetA;
var objA : TObjetA;
begin
  objA := TObjetA(objB.DonneObjetA);
  objA.Affiche;
end;


Créer de nouveaux types

Déclaration des types[modifier | modifier le wikicode]

Les déclarations de types commencent par le mot clé type, puis chaque nouveau type est déclaré selon la syntaxe suivante :

nom_type = type ;

Par exemple :

 type
     entier = integer;
     vraifaux = boolean;

Ensuite, les variables peuvent utiliser ces types :

 var i:entier;
     b:vraifaux;

Structure[modifier | modifier le wikicode]

Une structure est appelée record (enregistrement) en Pascal. Ce type regroupe plusieurs données, chacune portant un nom. Elle ne nécessite pas d'allocation de mémoire particulière, au contraire des objets (type class).

Exemple[modifier | modifier le wikicode]

 type
     point = record
         x , y : integer ;
     end record;

Une variable de ce type :

 var point_depart : point;

possède deux membres accessibles par l'opérateur point ( . ) :

 point_depart.x := 10;
 point_depart.y := 15;
GFDL GFDL Vous avez la permission de copier, distribuer et/ou modifier ce document selon les termes de la licence de documentation libre GNU, version 1.2 ou plus récente publiée par la Free Software Foundation ; sans sections inaltérables, sans texte de première page de couverture et sans texte de dernière page de couverture.