Un peu d'histoire

De la machine de Turing à UML, retrouvez les moments-clés de l'histoire de l'informatique dans cette série de slides : Image non disponibletéléchargez le document Projo-Histoire.pdfTéléchargez le document Projo-Histoire.pdf.

L'ordinateur

Un ordinateur est une machine utilisant et produisant de l'information. Il reçoit de l'information (de l'utilisateur, par internet…), donne de l'information (informations affichées à l'écran, imprimées…), mémorise de l'information (en mémoire vive, sur disque dur, clé USB, CD, DVD…) et traite de l'information (calcul mathématique, « raisonnement » logique…).

Nous allons donc commencer par parler de l'information et plus exactement de la manière dont elle est représentée dans un ordinateur.

Codage binaire

Le bit

Toute information traitée par un ordinateur est codée en binaire, c'est-à-dire par une suite de bits valant zéro ou un.

La représentation physique du zéro et du un dépend du type de support utilisé : mémoire vive, disque dur, clé USB, CD, etc… Mais, quel que soit le type de support utilisé, il s'agit toujours d'un point de vue logique, d'une suite de zéros et de uns.

Le transistor

Pour prendre un exemple, la représentation physique actuelle d'un bit en mémoire vive est un transistor composé de silicium. La représentation du zéro et du un est basée sur la répartition des charges négatives et positive dans le transistor.

Voici par exemple le zéro :

Image non disponible

Et voici le un :

Image non disponible

De 1970 à 2008, le nombre de transistors sur une puce électronique a augmenté de manière vertigineuse en suivant la loi de Moore : le nombre de transistors par puce double tous les deux ans pour le même prix de fabrication. Les transistors deviennent donc de plus en plus petits.

Image non disponible

Comme un signal électrique met moins de temps à parcourir une distance plus courte, cette progression a des répercussions directes sur la vitesse des processeurs (elle double tous les 18 mois). Elle entraîne également une augmentation constante de la capacité des mémoires vives (la RAM des PC double tous les deux ans environ).

En 2008, la taille minimale d'un transistor était de l'ordre de 45 milliardièmes de mètre.

L'octet et autres unités

Une suite de huit bits est appelé un octet (ou byte en anglais). C'est l'unité de base permettant de définir la dimension d'un espace de stockage. Il en dérive des unités plus grandes comme :

  • le K-Octet (Kilo octet = 210 octets = 1 024 octets) ;
  • le Méga-Octet (1 024 K, soit environ un million d'octets) ;
  • le Giga-Octet (1 024 Mégas, soit environ un milliard d'octets) ;
  • le Tera-Octet (1 024 Gigas, soit environ mille milliards d'octets).

La mémoire vive des ordinateurs actuels (2013) est de l'ordre de plusieurs Gigas alors que la capacité des disques durs se compte en Teras.

La manière de coder une information en binaire dépend du type d'information. Les informations numériques, par exemple, ne sont pas codées de la même manière que le texte ou que les images ou le son.

Pour fixer les idées, voyons deux exemples de codage.

Le codage des nombres entiers en base 2

Le codage binaire des nombres entiers est basé sur l'écriture des nombres en base 2 (en utilisant uniquement les chiffres 0 et 1).

Le principe est le même que celui de l'écriture d'un nombre entier en base 10. C'est-à-dire l'écriture habituelle d'un nombre utilisant les 10 chiffres de 0 à 9. Rappelons que dans ce système, chaque chiffre est associé à une puissance de 10. Il y a un chiffre pour les unités (100), un chiffre pour les dizaines (101), un pour les centaines (102), un pour les milliers (103), etc. Par exemple, 2013 signifie : 2 × 103 + 0 × 102 + 1 × 101 + 3 × 100.

L'écriture d'un nombre entier en base 2 utilise le même principe, sauf que les puissances de 10 sont remplacées par des puissances de 2. Le nombre 40, s'écrira par exemple : 101000. Car 40 = 32 + 8 ou autrement dit 25 + 23 et donc 40 = 1 × 25 + 0 × 24 + 1 × 23 + 0 × 22 + 0 × 21 + 0 × 20.

Comme 40 n'a que six chiffres en base 2, on peut représenter ce nombre sur un octet comme suit :

Image non disponible

Ce codage permet de coder de grands nombres en utilisant très peu de bits :

  • jusqu'à 64 000 environ avec 2 octets pour les entiers non signés (32 000 avec signe) ;
  • jusqu'à 4 milliards environ avec 4 octets pour les entiers non signés (2 milliards avec signe).

Le codage du texte en ASCII

Les informations textuelles (suite de caractères) sont souvent codés en ASCII. Dans ce codage, chaque caractère est représenté par un octet de valeur prédéfinie dans une table de correspondance.

La « valeur » est en fait l'interprétation du codage binaire de l'octet en nombre entier, comme nous l'avons vu au paragraphe précédent.

Prenons par exemple, le caractère '('. Dans la table de correspondance ASCII, on voit que sa valeur est 40. Autrement dit, le codage binaire du caractère '(' est :

Image non disponible

C'est-à-dire le même codage que celui du nombre 40 sur un octet !

Mais alors comment un ordinateur peut il distinguer un nombre d'un caractère ?

Il ne le peut pas !

En effet, la simple connaissance d'une suite de 0 et de 1 ne permet pas de savoir ce que représente cette suite. Tout dépend de la manière dont elle est interprétée.

Si on donne l'octet précédent à un ordinateur, il y verra le nombre 40, si à ce moment-là il s'attend à un nombre et il y verra le caractère '(', si à ce moment-là, il s'attend à un caractère.

Mémoire vive et adresse

Image non disponible

Barrette de RAM DDR3 de 4 Go

La mémoire vive (ou RAM pour Random Access Memory) d'un ordinateur peut être vue comme une suite continue d'octets.

Chaque octet possède un numéro. Le numéro du premier est 0, du deuxième 1, etc. Ce numéro s'appelle l'adresse de l'octet.

Une plage mémoire est une suite d'octets consécutifs. Elle peut donc être définie par une plage d'adresses (adresses minimale et maximale) ou bien par l'adresse de début (ou autrement dit, l'adresse minimale) de la zone et du nombre d'octets qu'elle contient.

Voici, par exemple, une plage mémoire de trois octets, démarrant à l'adresse 2 :

Image non disponible

Langage machine et programme exécutable

Image non disponible

Processeur Intel 80486DX2 (taille: 12 × 6,75 mm)

Le processeur d'un ordinateur exécute en permanence des instructions que l'on appelle instructions machine. Il contient (en autre) des mémoires de petite taille (quelques octets), appelées registres, qui servent à transférer des données de la mémoire vers le processeur (ou inversement) en vue d'effectuer des opérations sur ces données.

Une instruction machine est un ordre donné au processeur.

Par exemple :

  • copier une plage mémoire d'adresse donnée dans un registre ;
  • copier le contenu d'un registre dans une plage mémoire d'adresse donnée ;
  • additionner, soustraire, diviser ou multiplier les nombres contenus dans deux registres et enregistrer le résultat dans un troisième registre.

Nous avons vu plus haut que toute information traitée par un ordinateur est codée en binaire. Il en de même des instructions machine. Une instruction machine est codée en binaire sur quelques octets (4 ou 8 dans les machines actuelles). Une partie du codage définit le type d'opération à effectuer et une autre, les objets (les registres, par exemple) sur lesquels cette opération doit agir.

La manière de coder les instructions machine dépend du type de processeur.

On appelle langage machine l'ensemble de conventions définissant le codage binaire des instructions machine d'un type particulier de processeur.

Une suite d'instructions machine constitue un programme ou plus précisément un programme exécutable.

Il est important de noter que le langage machine est le seul langage directement exécutable par un ordinateur. Les langages de programmation (comme C, Java, Php…) ne le sont pas !

Les programmes exécutables peuvent se trouver en mémoire (on dit qu'ils sont résidents) ou sur le disque, sous la forme de fichiers (reconnaissables sous Windows par leur extension .exe). Mais pour pouvoir être exécutés, ils doivent forcément être chargés en mémoire :

Image non disponible

Nous avons dit plus haut qu'un programme exécutable est une suite d'instructions.

En fait, pour être plus exact, il faudrait définir un programme exécutable comme un mélange de zones de code contenant des instructions machine et de zones de données servant à stocker provisoirement les informations traitées (données ou résultats).

Image non disponible

Processus

La mémoire d'un ordinateur contient plusieurs programmes que le processeur exécute à tour de rôle à une telle vitesse que l'on a l'impression qu'ils sont exécutés en même temps.

Chacun de ces programmes résidents possède donc ses propres zones d'instructions et de données.

Image non disponible

L'exécution d'un programme particulier par un ordinateur s'appelle un processus.

Notez qu'un même programme peut donner lieu à plusieurs processus.

Parmi les processus résidents figurent nécessairement les processus système, qui servent (entre autres) à gèéer les périphériques (clavier, souris, écran, disque, lecteur de CD, imprimantes, etc) et les fichiers.

Les entrées-sorties

Jusqu'à présent, nous avons essentiellement décrit l'ordinateur du point de vue du processeur et de la mémoire vive. Le processeur exécute en permanence des programmes chargés en mémoire. Ces programmes sont des suites d'instructions machines et de plage mémoires réservées aux données.

Mais si les instructions exécutées par le processeur ne font que transférer des informations entre la mémoire vive et les registres, comment peut-il communiquer avec le clavier ? avec la souris ? avec l'écran ?

Comment les plages mémoire réservées aux données peuvent-elles recevoir les données de l'utilisateur ?

Comment peuvent-elles s'afficher à l'écran ?

Image non disponible

Pour répondre à ces questions, il nous faut tout d'abord compléter notre vision d'un ordinateur et considérer les éléments externes à l'unité centrale que l'on appelle les périphériques (écran, clavier, souris, disque, lecteur de CD, clé USB, imprimante, etc).

Les périphériques ont leurs propres circuits spécialisés appelé interfaces d'entrées-sorties. Pour l'écran, par exemple, il s'agit de la carte graphique. Ces circuits peuvent avoir leurs propres registres, leur propre mémoire et même leurs propres processeurs.

Ils communiquent avec le processeur et la mémoire par un ensemble de fils que l'on appelle le bus de données.

En réalité, les instructions exécutées par le processeur ne se limitent pas à la mémoire centrale. Certaines instructions spéciales, appelées instructions d'entrée-sortie, permettent de transférer des données entre les interfaces d'entrée-sortie et les registres du processeur ou directement avec la mémoire vive.

Image non disponible

Ceci explique très sommairement comment se fait la communication entre le processeur et les périphériques et indirectement, comment un programme peut envoyer des informations vers un périphérique ou recevoir des informations provenant d'un périphérique.

Mais cela n'explique pas l'interaction avec l'utilisateur. En effet, vous remarquerez que votre ordinateur réagit instantanément (ou quasiment) lorsque vous éjectez un CD, introduisez une clé USB, bougez la souris, appuyez sur une touche du clavier, etc.

Ces actions physiques de l'utilisateur sur l'ordinateur peuvent se produire à n'importe quel moment, alors que le processeur est occupé à exécuter les instructions machine d'un programme quelconque. Or c'est nécessairement le processeur qui doit réagir à ces actions.

Il faut donc qu'il soit capable d'interrompre l'exécution du programme en cours pour réagir immédiatement à n'importe quelle action de l'utilisateur.

Comment cela est-il possible ?

Les interruptions

La réponse à cette question se trouve dans le mécanisme d'interruption.

Une interruption (physique) est un signal envoyé au processeur par un dispositif externe à l'unité centrale.

Le clavier va par exemple envoyer un signal d'interruption au processeur chaque fois qu'une touche est appuyée. La souris envoie une interruption chaque fois qu'elle est déplacée ou que l'utilisateur effectue un clic.

Le processeur possède des entrées spécialement prévues pour recevoir ces signaux.

Lorsqu'il reçoit une interruption, il interrompt provisoirement le programme qu'il est en train d'exécuter pour exécuter des instructions du système d'exploitation prévues, pour traiter ce type d'interruption. Il existe par exemple des instructions prévues pour traiter les interruptions claviers, d'autres pour traiter des interruptions souris, etc.

Image non disponible

Le système ne gère pas entièrement les interruptions ; elles sont sous-traitée par les pilotes (drivers en anglais) des périphériques. Un pilote est un programme spécialisé dans la gestion d'un périphérique spécifique. Il ne fait pas partie du système d'exploitation car il est généralement produit par le fabricant du périphérique.

Une fois l'interruption traitée, le processeur reprendra ce qu'il était en train de faire à l'endroit exact où il s'était arrêté.

Pour donner une image, on pourrait comparer ce mécanisme au réflexe humain. Prenons par exemple une personne lisant un livre. Le cerveau de cette personne joue le rôle du processeur. Le programme exécuté est la lecture du livre. Soudain, une abeille pique la personne. La douleur perçue est le signal d'interruption envoyé au cerveau (le processeur). Par réflexe, la personne lâche le livre. C'est une réaction préprogrammée dans l'inconscient (le système d'exploitation). Après avoir soigné la piqûre (traitement de l'interruption), la personne reprend la lecture du livre à l'endroit où elle s'était arrêtée.

Les Interface Graphiques

Historique

Les premières interfaces graphiques sont apparues avec l'invention de la souris (1967) et des écrans graphiques, c'est-à-dire capables d'afficher des images.

Avant cela, les écrans d'ordinateur n'affichaient que du texte et ressemblaient plutôt à ceci :

Image non disponible

En 1973 apparaît le premier ordinateur avec interface graphique : la station graphique Xerox Alto fabriquée en Californie, à Palo Alto, par la société Xerox (par ailleurs à l'origine de la machine à photocopier).

Image non disponible

En 1983, cet ordinateur a inspiré le premier PC de Apple, le LISA, ancêtre des Macintosh puis en 1985, le premier système Windows (Windows 1.0) de Microsoft.

Image non disponible

C'est ainsi que sont apparus les premiers systèmes d'exploitation munis d'interfaces graphiques, dans lesquels les applications apparaissent sous forme de fenêtres ou d'icônes sur lesquelles on peut agir grâce à la souris.

Interface du système

De manière générale, l'interface graphique d'un programme est la manière dont il se présente visuellement à l'écran lorsqu'il s'exécute.

Dans cette partie, nous décrivons plus particulièrement l'interface graphique du système qui apparaît au démarrage d'un ordinateur. Dans Windows, par exemple, elle est constituée du bureau et d'une barre de tâches.

Image non disponible

Le bureau contient des icônes de différents programmes. La barre des tâches donne accès (entre autres) au menu Démarrer, par l'intermédiaire duquel on peut accéder à différents composants du système, configurer l'ordinateur, arrêter l'ordinateur etc.

Tout ceci constitue l'interface graphique du système d'exploitation.

Le curseur

On voit également apparaître le curseur, qui se présente généralement sous la forme d'une petite flèche. Le curseur permet d'indiquer sur quel élément de l'interface graphique on veut agir.

Lorsque l'utilisateur déplace la souris, il provoque une interruption gérée par le système d'exploitation. Le traitement par défaut de cette interruption consiste à déplacer le curseur.

Gestion du clic

Comme nous l'avons vu, un clic provoque une interruption, qui est tout d'abord interceptée par le système d'exploitation. Le traitement de cette interruption dépendra de la position du curseur. Nous reviendrons là-dessus plus en détail un peu plus loin.

Lorsque l'on clique sur l'icône d'un programme, par exemple, le système le charge en mémoire et lance son exécution. L'interface graphique de ce programme s'affiche alors à l'écran.

Fenêtre d'une application

L'interface graphique d'un programme se présente généralement sous la forme d'un rectangle, appelé fenêtre, dans lequel apparaissent des composants ou contrôles. Les composants sont par exemple des menus, des barres d'outils, des zones de texte…

Voici par exemple une partie de l'interface graphique d'Excel 2003 :

Image non disponible

Les zones de texte sont des zones rectangulaires de l'écran dans lesquelles il est possible de taper du texte sur une ligne. Nous reviendrons plus en détail sur ce composant graphique.

À un moment donné, l'écran contient une (ou plusieurs) fenêtre(s) pour chaque application ouverte. Par exemple, une fenêtre pour Internet Explorer, une fenêtre pour Word, une autre pour Photoshop, etc. Mais parmi toutes ces fenêtres, il y a une seule fenêtre active : c'est la fenêtre qui apparaît au premier plan et qui contient le curseur.

Image non disponible

Les évènements

Lorsqu'il se produit une interruption souris (déplacement de la souris ou clic) ou clavier (touche appuyée ou relâchée), il y a deux possibilités :

  1. Il n'y a aucune fenêtre active. Dans ce cas, l'interruption est directement gérée par le système. Sous Windows, c'est par exemple le cas lorsque vous cliquez sur le bureau.
  2. Il y a une fenêtre active. Dans ce cas, le système intercepte tout d'abord l'interruption, mais il ne la gère pas forcément entièrement. Une partie de la gestion de l'interruption est sous-traitée à l'application associée à cette fenêtre. Pour cela, il communique toutes les informations utiles à l'application sous la forme d'un évènement. Les parties d'une l'application qui interceptent les évènements et les gèrent sont appelés des gestionnaires d'évènement.

Image non disponible

Un évènement est lié à un composant particulier de la fenêtre, que l'on pourrait appeler le composant actif : c'est le composant contenant le curseur au moment de l'évènement.

Une application pourra ainsi savoir sur quel composant l'utilisateur a cliqué ou quel était le composant actif lorsqu'il a appuyé sur telle touche du clavier.

Les zones de texte

Les interfaces graphiques sont généralement conçues pour éviter au maximum de taper du texte. Mais il arrive que ce soit inévitable. Par exemple, pour donner le nom d'un fichier, une adresse web, une recherche par mots clés, etc.

Dans une interface graphique, la saisie de texte se fait par l'intermédiaire des zones de texte, zones rectangulaires dans lesquelles il est possible de « rentrer » du texte.

Pour cela, l'utilisateur doit tout d'abord activer la zone de texte en cliquant dedans. Le curseur change alors de forme. En général, il se transforme en une barre verticale clignotante, ce qui signifie que la zone de texte est active et prête à recevoir du texte.

Lorsqu'une zone de texte est active, les caractères frappés au clavier viennent s'afficher à l'écran à l'intérieur de celle-ci. On a ainsi l'impression que les caractères sont directement transférés du clavier vers l'écran. En réalité, il sont tout d'abord transférés en mémoire par le système d'exploitation.

En effet, à chaque zone de texte est associée une plage mémoire, appelée buffer, destinée à mémoriser les caractères frappés.

Supposons qu'une zone de texte soit active et que l'utilisateur appuie sur la touche « s ». Le système va alors transférer le caractère 's' dans le buffer associé à cette zone de texte, puis dans un deuxième temps, afficher le contenu du buffer à l'écran à l'intérieur de la zone de texte.

Image non disponible

S'il appuie ensuite sur la touche « t », le caractère 't' va être rajouté dans le buffer après le 's', puis le buffer va être réaffiché.

Ce mécanisme est très important, car c'est essentiellement de cette manière que des données sont communiquées à un programme. Nous reviendrons là-dessus lorsque nous parlerons de la lecture des données.

La programmation

Les langages de programmation ont été introduits pour éviter l'écriture directe de programmes en langage machine (tâche extrêmement fastidieuse, pour ne pas dire impossible !).

Mais tout d'abord, qu'est-ce qu'un programme ?

Notion de programme

Un programme écrit dans un langage de programmation se présente sous la forme d'un texte dans lequel figurent les instructions à exécuter. Dans ce texte, les plages mémoire contenant des données portent un nom. Ce sont les variables du programme.

Voici par exemple une instruction écrite en pascal :

 
Sélectionnez
  somme := x + y;

Cette instruction signifie : additionner les nombres contenus dans les variables x et y et stocker le résultat dans la variable somme.

Dans les cours de programmation, un programme est souvent défini comme une suite d'instructions. Cette définition n'est pas tout-à-fait exacte car les programmes contiennent également des déclarations. Une déclaration permet (entre autres) de définir la nature des variables sur lesquelles le programme doit travailler.

Voici par exemple une déclaration de variable écrite en pascal :

 
Sélectionnez
var x, y , somme : integer;

Ici le programmeur a déclaré que les variables x, y et somme doivent contenir des nombres entiers.

Notion de langage

Un langage de programmation est un ensemble de conventions définissant la manière d'écrire un programme sous la forme d'un texte.

Ces conventions comprennent des conventions syntaxiques (comparables à la grammaire d'une langue naturelle) et lexicales (comparables à l'orthographe d'une langue naturelle).

Elles sont évidemment différentes d'un langage à l'autre. Voici par exemple l'instruction d'addition vue plus haut traduite dans différents langage de programmation :

Image non disponible

On constate que dans certains langages (comme PHP et LISP), les déclarations de variables ne sont pas obligatoires. Ces langages sont dits non typés.

Tous les langages de programmation possèdent des mots spéciaux, que l'on appelle des mots-clés (dans l'exemple ci-dessus, ces mots figurent en gras). Les mots-clés ont une signification particulière prédéfinie dans le langage de programmation. Ils ne peuvent donc pas être utilisés par le programmeur pour signifier autre chose.

En Pascal, par exemple, le mot clé Integer signifie nombre entier.

Fichiers sources

Un programme écrit dans un langage de programmation se présente sous la forme d'un ou plusieurs fichiers appelés fichier sources (par opposition aux fichier exécutables).

Ces fichiers contiennent les programmes tels qu'ils ont été initialement écrits par les développeurs. Ce sont des textes écrits en un langage de programmation particulier, que l'on peut visualiser à l'aide d'un éditeur de texte (WordPad, par exemple).

Mais, comme nous l'avons déjà dit plus haut, le seul langage de programmation directement exécutable par un ordinateur est le langage machine adapté à son processeur.

Les fichiers sources doivent donc être traduits en langage machine avant de pouvoir être exécutés. On appelle cela la compilation. Mais nous verrons plus loin que la compilation n'est pas la seule approche possible : il en existe une autre appelée interprétation.

Compilation

Notion de compilateur

Certains langages, comme le C par exemple, ne peuvent être que compilés. Les langages de ce type sont appelés langages compilés. Pour pouvoir les utiliser, vous devez avoir installé sur votre ordinateur un compilateur spécifique à ce langage.

Image non disponible

Le compilateur est un programme exécutable qui permet de traduire un programme écrit dans un langage de programmation en langage machine.

Par exemple, pour pouvoir programmer en C, vous devez nécessairement avoir installé un compilateur C sur votre machine.

Après avoir été compilé, le programme peut être exécuté en chargeant sa version exécutable en mémoire  :

Image non disponible

Exemple de compilation

Pour fixer un peu les idées, reprenons le programme d'addition en Pascal et voyons comment il pourrait être traduit en langage machine.

Les variables déclarées sont tout d'abord associées à des plages mémoire :

Image non disponible

Les instructions sont ensuite traduites en instructions machine portant sur les registres et les plages mémoire associées aux variables :

Image non disponible

Dans cet exemple, nous n'avons pas donné le code binaire des instructions machine car il serait sans intérêt. On voit qu'une simple instruction écrite en langage de programmation peut correspondre à de nombreuses instructions machine.

Interprétation

Les programmes écrits en langages interprétés (Java, Visual Basic, PHP…) peuvent être exécutés sans avoir été auparavant entièrement traduits en langage machine.

Ils font appel à un interpréteur.

Tout comme un compilateur, un interpréteur est spécifique à un langage donné.

Il existe, par exemple, un interpréteur Java, un interpréteur Visual Basic, un interpréteur PHP

Contrairement aux programmes compilés, les programmes interprétés ne sont pas directement exécutés par l'ordinateur, mais par l'interpréteur (qui, lui, est exécuté par la machine !).

Pour exécuter un programme interprété, il faut tout d'abord lancer l'interpréteur, puis charger le code source du programme en mémoire. Ce code source peut ensuite être exécuté par l'interpréteur :

Image non disponible

L'interpréteur s'exécute donc toujours « en même temps » que le programme interprété.

Notez que certains interpréteurs (comme Visual Basic, par exemple), peuvent également fonctionner en tant que compilateur. Cela permet d'obtenir une version plus rapide et commercialisable du programme, lorsque la mise au point est terminée. En effet :

  • l'exécution d'un programme compilé est beaucoup plus rapide que son interprétation ;
  • sous sa forme exécutable, un programme peut être vendu sans divulguer les fichiers sources, ainsi qu'à des utilisateurs ne possédant pas l'interpréteur sur leur machine.

Les EDIs

La développement d'application (ou si vous préférez la programmation) se fait aujourd'hui grâce à des logiciels appelés EDI (pour Environnement de Développement Intégré) ou IDE en anglais. Un EDI peut être adapté à un langage de programmation particulier (Delphi, Lazarus pour le langage Pascal, Visual C++ pour le langage C++) ou à plusieurs langages (Eclipse pour Java, PHP, Javascript).

Un EDI comprend en général un éditeur de texte spécialement adapté au langage, un compilateur et/ou un interpréteur, un débogueur (outil de mise au point) et, surtout, un outil de développement d'interface graphique.

Avant l'existence des EDIs, la conception de l'interface graphique d'un programme était extrêmement fastidieuse car le développeur devait lui même préciser dans le programme les positions et dimensions de tous les composants de l'interface.

Avec un EDI cela se fait simplement en « dessinant » en quelque sorte l'interface graphique à l'aide de la souris. Évidemment, les instructions permettant d'afficher l'interface existent toujours, mais au lieu d'être données par le programmeur, elles sont générées automatiquement par l'EDI.

La programmation évènementielle

Avec l'apparition des interfaces graphiques dans les années 80, la programmation est devenu plus complexe. Les programmes devaient d'une part produire tout le design de l'interface et d'autre part pouvoir réagir aux évènements, c'est-à-dire aux actions de l'utilisateur sur les différents composant de cette interface.

Il est alors apparu une nouvelle manière de programmer, appelé programmation évènementielle, qui consiste à associer des gestionnaires d'évènement aux composants de l'interface.

Image non disponible

Un des premiers langages permettant la programmation évènementielle a été le langage Visual Basic de Alan Cooper (première version en 1991) et son environnement de développement intégré (un des premiers également). Le concept a ensuite été repris par Borland avec Delphi.

Image non disponible

Alan Cooper - inventeur du Visual Basic

Un des points les plus intéressants des EDIs est qu'ils facilitent la programmation évènementielle. Rappelons qu'avec un EDI, l'interface graphique peut être « dessinée » par le programmeur. Il peut ensuite associer un gestionnaire d'évènement (initialement vide) à un composant de l'interface en cliquant sur celui-ci.

Lazarus

Image non disponible

Lazarus est un environnement de développement intégré associé au langage Pascal Objet. C'est la version open source du logiciel Delphi de Borland. Ce logiciel facilite en particulier la création d'interface graphique et la programmation évènementielle par un mécanisme de génération automatique de code.

Notion de formulaire

Sous Lazarus, une fenêtre s'appelle un formulaire.

Voici, par exemple, un formulaire réalisé avec Lazarus :

Image non disponible

Ce formulaire comporte trois étiquettes, trois zones de texte et un bouton.

Image non disponible

Nous avons vu que les zones de texte permettent à l'utilisateur de saisir des données. En fait, elles servent également à afficher des résultats.

Image non disponible

Dans notre exemple, les deux zones de texte étiquetées X et Y servent à saisir les deux nombres à additionner. Ce sont les données du programme. La zone de texte étiquetée X+Y sert à afficher la somme de ces deux nombres. C'est le résultat du programme produit par l'ordinateur.

Les étiquettes servent donc en principe à indiquer à l'utilisateur quelle donnée il doit introduire dans la zone de texte située juste à côté ou bien quel résultat y sera affiché.

Lorsque l'utilisateur clique sur le bouton Additionner, après avoir saisi les deux nombres dans les zones de texte prévues à cet effet, le programme affiche le résultat dans la zone de texte étiquetée X+Y.

Un bouton sert donc à déclencher un traitement d'information particulier (dans notre cas, l'addition de deux nombres).

Génération automatique de code

La création d'une nouvelle application engendre automatiquement la création d'un formulaire (initialement vide) associé à un fichier source Pascal appelé unité (Unit en anglais).

Image non disponible

Le formulaire de départ

Par défaut, ce formulaire s'appelle Form1 et l'unité Pascal associée, Unit1. Si le développeur ne la renomme pas autrement, elle sera représentée sur disque par le fichier Unit1.pas.

Dès la création du formulaire, l'unité qui lui est associée contient du code Pascal généré automatiquement.

Le voici :

Image non disponible

Au fur et à mesure que le programmeur ajoute des composants sur le formulaire, leurs noms apparaissent automatiquement dans le code source. Avec l'exemple précédent du programme d'addition, ce serait Label1, Label2, Label3 pour les étiquettes ; Edit1, Edit2, Edit3 pour les zones de texte ; Button1 pour le bouton.

Image non disponible

Voici le code source de l'unité associée au programme d'addition, après que le programmeur ait déposé les différents composants sur le formulaire :

Image non disponible

Ces noms permettront d'accéder aux composants par programme. Il ne faut pas les confondre avec leurs libellés. Les libellés apparaissent sur l'interface graphique. Les noms quant-à eux sont invisibles.

Par exemple, le nom du bouton est Button1, mais son libellé est Additionner.

Pour pouvoir plus facilement identifier les composants dans le programme, le développeur peut leur donner des noms plus significatifs que ceux générés automatiquement par Lazarus. Par exemple, BoutonAdditionner au lieu de Button1 pour le bouton, ZoneTexteX et ZoneTexteY pour les zones de texte contenant les deux nombres à additionner, ZoneTexteSomme pour la zone de texte contenant la somme des deux nombres.

La modification des noms des composants se fait via l'interface graphique de Lazarus. Ils sont alors automatiquement modifiés dans le code.

Voici le code modifié automatiquement après modification des noms :

Image non disponible

Programmation évènementielle

L'interface graphique de Lazarus est conçue pour faciliter la programmation évènementielle.

Rappelons que la programmation évènementielle consiste à associer des gestionnaires d'évènement à des évènements de l'interface graphique. Un évènement est définit par un composant de l'interface et un type d'interruption. Par exemple, un bouton (composant) et un clic sur ce bouton (interruption). Lorsque cet évènement a effectivement lieu, les instructions contenues dans le gestionnaire d'évènement sont automatiquement exécutées.

Sous Lazarus, un gestionnaire d'évènement s'appelle une procédure évènementielle.

Pour associer une procédure évènementielle à un composant, le développeur n'a besoin de taper aucune ligne de code. Il lui suffit en gros de cliquer sur le composant et éventuellement de sélectionner le type d'interruption. Un double clic sur un composant sélectionne l'interruption par défaut. La déclaration de la procédure évènementielle est alors automatiquement générée dans le code avec un nom formé à partir du nom du composant et du nom de l'interruption.

Reprenons l'exemple de notre programme d'addition.

Supposons que le développeur souhaite associer une procédure évènementielle au bouton Additionner, qui serait activée lorsque l'utilisateur clique sur ce bouton.

Pour cela, il lui suffit de faire un double clic sur ce bouton et la déclaration de cette procédure évènementielle sera automatiquement générée dans le code source, avec aucune instruction à l'intérieur.

Image non disponible

Le nom de cette procédure est BoutonAdditionnerClick. Il est formé à partir du nom du composant (ici BoutonAdditionner) et du nom de l'interruption (Click = clic de souris).

Le développeur complètera ensuite ce gestionnaire d'évènement en plaçant des instructions à l'intérieur (entre begin et end), qui seront automatiquement exécutées lorsque l'utilisateur cliquera sur ce bouton.

Ordre d'exécution des instructions

En général, un programme ne s'exécute pas dans l'ordre des lignes du code source, c'est-à-dire de la première à la dernière ligne du fichier.

Considérons un programme dont l'interface graphique possède plusieurs trois boutons A, B et C :

Image non disponible

Dans le code source, on a d'abord la procédure évènementielle du bouton B, puis celle du bouton A, puis celle du bouton C. Mais l'ordre d'exécution de ces procédures est totalement indépendant de l'ordre dans lequel elles figurent dans le fichier source. Cela dépend évidemment de l'ordre dans lequel l'utilisateur cliquera sur les boutons A, B et C. Une même procédure peut même être exécutée plusieurs fois.

Lorsqu'une procédure évènementielle a fini de s'exécuter, le programme retourne dans un état d'attente d'évènement.

Notice d'utilisation

Téléchargez ici la notice d'utilisation de Lazarus.

Les variables

La notion de variable

La notion de variable existe dans tous les langages de programmation.

Les variables servent à conserver temporairement des données en mémoire.

Une variable peut être vue comme une plage mémoire dans une zone de données d'un programme. Elle porte un nom et possède à un instant donné une valeur.

On peut donc parler de l'adresse d'une variable : c'est l'adresse du début la plage mémoire qui lui est réservée.

Voici par exemple quatre variables nommées Age, Nom, NombreEnfant et Prenom.

Image non disponible

La valeur d'une variable varie lors de l'exécution d'un programme (d'où le nom de variable justement !). La variable NombreEnfant pourra par exemple valoir successivement 2, 0, 3, 1, 0, 4…

Par contre, le nom d'une variable ne change pas : il est le même du début à la fin de l'exécution d'un programme.

Les noms de variables en Pascal

En Pascal, les noms de variables doivent respecter les règles suivantes :

Règle1 : Le premier caractère est obligatoirement une lettre.

Règle2 : Les seuls caractères autorisés sont les lettres (sans accents), les chiffres et « _ ».

Règle3 : un nom de variable ne peut pas être un mot clé du langage.

Voici quelques exemples et contre-exemples de nom de variables :

Nom Validité Règles non respectées
Prenom Oui  
Prénom Non 2
Nom101 Oui  
Nom_Eleve Oui  
Nom Eleve Non 2
_Age Non 1
Age-Eleve Non 2
Integer Non 3

Attention : la casse n'a pas d'importance, c'est-à-dire que le Pascal ne distingue pas les minuscules des majuscules. Nom_Eleve, nom_Eleve, nom_eleve désignent donc la même variable.

La notion de type

Il existe différents types de variables.

Le type d'une variable définit la manière dont elle est représentée en mémoire et les opérations que l'on peut faire dessus.

Il existe, par exemple, des variables de type chaîne de caractères, qui vont servir à stocker du texte en mémoire. La variable Prenom pourrait, par exemple, être de ce type. À un instant donné, sa valeur pourrait être 'Astérix', à un autre moment 'Obélix' ou encore à un autre moment 'Abraracourcix'.

Il existe aussi des variables de type entier. Ce pourrait, par exemple, être les variables Age, ou NombreEnfant.

La représentation en mémoire d'une variable dépend de son de type. En particulier, deux variables de types distincts n'occupent pas en général la même place mémoire :

Image non disponible

D'autre part, les opérations que l'on peut faire sur deux variables de types différents ne sont pas les mêmes. Par exemple, on ne pourra pas multiplier deux chaînes de caractères, alors que l'on peut très bien multiplier deux entiers.

Déclaration d'une variable

La déclaration d'une variable sert à définir son type et, par conséquent, la manière dont elle sera représentée en mémoire. Elle a pour effet de réserver une plage mémoire pour stocker la valeur de cette variable. Le type de la variable définit la taille de cette plage et la manière de coder ses valeurs.

Toute variable doit (en principe) être déclarée, avant de pouvoir être utilisée. En Pascal, la déclaration est obligatoire. D'autres langages (comme Visual Basic, par exemple) autorisent une déclaration implicite avec un type par défaut. Malheureusement, cela autorise également des erreurs de programmation…

La manière de déclarer une variable dépend évidemment du langage de programmation, mais on y trouve toujours les mêmes ingrédients : le nom de la variable et son type.

Déclaration d'une variable en Pascal

Syntaxe générale

Une déclaration de variable en Pascal utilise le mot clé Var suivi du nom de la variable, du caractère « : », puis du nom du type et d'un point-virgule.

 
Sélectionnez
Var Nom de la variable : nom du type ;

Déclaration d'un entier

Prenons par exemple la variable Age.

En Pascal, le type entier s'appelle Integer. La déclaration de la variable Age se ferait donc comme ceci :

 
Sélectionnez
Var Age : Integer ;

Le type double

Les nombres entiers ne sont pas les seuls nombres avec lesquels un ordinateur peut calculer. Il y a également les nombres décimaux (comme 0.5 ou 3.14 par exemple). Ces nombres peuvent par exemple (il y a d'autres possibilités) être représentés par le type Double en Pascal.

Déclaration d'une chaîne de caractères

Prenons par exemple la variable Prenom, de type chaîne de caractères.

En Pascal, le type chaîne de caractères s'appelle String. La déclaration de la variable Prenom s'écrira donc :

 
Sélectionnez
Var Prenom : String ;

Regroupement des déclarations

Remarquez qu'il est inutile de répéter le mot clé var. De plus, les variables de même type peuvent être regroupées : on sépare les noms des variables par des virgules puis on précise leur type par : nom du type ;

Exemple :

 
Sélectionnez
Var Nom, Prenom : String ; Age, NombreEnfant : Integer;

Où faut-il déclarer les variables ?

Pour qu'une variable soit utilisable à n'importe quel endroit d'une unité Pascal, elle doit être déclarée entre var et implementation, comme dans l'exemple suivant :

 
Sélectionnez
var
  Form1: TForm1;
  // Déclaration des variables globales
  x, y , somme : integer;

implementation

Nous avons déclaré ici trois variables de type entier : x, y et somme.

Les variables déclarées de cette manière sont appelée variables globales. Cela signifie qu'elles peuvent être utilisées dans tout le code du fichier où elles sont déclarées.

Lecture et affichage

Données et résultats d'un traitement

De nombreux traitements informatiques peuvent se schématiser suivant les trois étapes suivantes :

Image non disponible

L'image du sourd-muet qui ne sait pas additionner

Pour bien comprendre le sens des termes lecture, traitement et affichage, mettez-vous à la place de l'ordinateur. Votre rôle est d'additionner mentalement deux nombres pour un sourd-muet qui ne sait pas calculer (l'utilisateur).

Comme il ne peut pas parler, il vous écrit les deux nombres à additionner sur un bout de papier, qui joue le rôle de l'écran.

Vous ne pouvez pas additionner les deux nombres si vous ne les connaissez pas. Il faut donc commencer par les lire. C'est la première étape de lecture des données.

À présent, les deux nombres à additionner sont dans votre mémoire. Vous pouvez donc effectuer le traitement, qui consiste à additionner mentalement ces deux nombres. C'est l'étape de traitement des données.

Vous avez le résultat. Mais si vous ne le communiquez pas à l'utilisateur, c'est comme si vous n'aviez rien fait. Comme il est sourd, vous allez écrire le résultat sur le bout de papier. C'est la troisième étape : l'affichage des résultats du traitement.

Pour un ordinateur

Pour un ordinateur c'est un peu la même chose :

  1. Lire les données signifie transférer des informations affichées à l'écran vers la mémoire, ou plus précisément vers une variable-donnée, c'est-à-dire une variable prévue pour stocker une donnée du traitement.
    Image non disponible
  2. Traiter les données signifie grosso-modo faire des calculs qui dépendent des données pour produire des résultats. En principe, mais ce n'est pas obligatoire, chaque résultat est stocké dans une variable-résultat. La programmation de cette étape sera vue plus loin dans le cours lorsque nous parlerons d'expression et d'affectation.
    Image non disponible
  3. Si les résultats ont été sauvegardés dans des variables-résultats, Afficher les résultats signifie afficher la valeur de chacune de ces variables à l'écran. Il est également possible d'afficher les résultats d'un traitement sans les avoir au préalable sauvegardés dans des variables. Mais il serait difficile pour l'instant d'expliquer comment. Nous verrons cela un peu plus loin.
    Image non disponible

Exemple du programme d'addition

Reprenons l'exemple de l'addition de deux nombres.

Lire les données signifierait dans ce cas lire les valeurs des deux nombres à additionner depuis (par exemple) deux zones de texte pour les stocker dans deux variables données X et Y.

Image non disponible

Traiter les données signifierait additionner les valeurs des variables X et Y, puis mémoriser le résultat dans une variable Somme.

Image non disponible

Enfin, afin afficher les résultats signifierait afficher la valeur de la variable somme dans la zone de texte prévue à cet effet.

Image non disponible

Historique

Avant l'apparition des interfaces graphiques, tous les langages de programmation avaient des procédures (cette notion sera abordée dans le chapitre sous-programmes) pour lire et afficher.

En Pascal, par exemple, elles s'appellent readln (pour la lecture) et writeln (pour l'écriture).

La procédure writeln

La procédure writeln vous permettait d'afficher un texte à l'écran. Par exemple l'instruction :

 
Sélectionnez
writeln ('Hello world !');

vous affichait le texte « Hello Word » à l'écran, comme ceci (en supposant que l'écran était vide avant l'exécution de l'instruction) :

Image non disponible

Avec la procédure writeln, vous pouviez également afficher la valeur d'une variable à l'écran. Par exemple, en supposant que la variable Somme contienne la valeur 2013, l'instruction :

 
Sélectionnez
writeln (Somme);

vous affichait « 2013 » à l'écran, comme ceci :

Image non disponible

Chaque writeln affichait donc une nouvelle ligne à la suite des lignes déjà affichées à l'écran.

La procédure readln

La procédure readln permettait de lire la valeur d'une variable tapée au clavier (et donc affichée à l'écran).

Supposons par exemple qu'un programme exécute l'instruction suivante :

 
Sélectionnez
readln (X);

X représente un nombre entier.

Dès que l'instruction est exécutée, le programme est momentanément interrompu. Le système d'exploitation prend alors la main et range chaque caractère saisi par l'utilisateur dans un buffer associé au clavier. Dès que l'utilisateur appuie sur la touche Entrée, le contenu du buffer est renvoyé au programme qui stocke alors la valeur correspondante dans la variable X et continue son exécution.

ETBib

ETBib est une unité Pascal que j'ai écrite afin d'introduire des procédures équivalentes à readln et writeln fonctionnant avec une interface graphique.

Ces procédures sont les suivantes :

  • Lire, LireEntier et LireNombre : pour lire une variable depuis une zone de texte ;
  • Afficher, AfficherEntier, et AfficherNombre : pour afficher une variable dans une zone de texte ou une zone de liste ;
  • Effacer : pour effacer le contenu d'une zone texte ou d'une zone de liste.

Ces procédures ont été introduites pour des raisons pédagogiques (explications pour le lecteur averti). Donc gardez bien à l'esprit qu'elles ne font pas partie du langage Pascal.

Exemple d'utilisation des procédures de ETBib

Pour expliquer le fonctionnement des procédures de ETBib, nous allons prendre un exemple de programme qui permet de lire et d'afficher trois variables :

  • Ville, de type String, est destinée à contenir le nom d'une ville. Sa valeur sera lue et affichée dans la zone de texte ZT_Ville ;
  • CP, de type integer, pour le code postal de la ville, lue et affichée dans la zone de texte ZT_CodePostal ;
  • NH, de type double, pour le nombre d'habitants de la ville exprimé en milliers. Remarquez que ce nombre n'est pas forcément entier : 264,2 représente par exemple 264 200 habitants. La zone de texte correspondante se nomme ZT_Hab.

Voici l'interface graphique du programme :

Image non disponible

Lecture des variables

La lecture des trois variables est déclenchée par le bouton Lire. Voici la procédure évènementielle associée :

 
Sélectionnez
procedure TForm1.BT_LireClick(Sender: TObject);
begin
  Lire (Ville,ZT_Ville);
  LireEntier (CP, ZT_CodePostal);
  LireNombre (NH,ZT_Hab);
end;

La procédure de lecture à utiliser dépend du type de la variable : Lire pour lire une variable de type String, LireEntier pour lire une variable de type Integer et LireNombre pour lire une variable de type Double.

Entre parenthèses figurent :

  • en première position : le nom de la variable à lire ;
  • en deuxième position : le nom de la zone de texte.

Rappelez-vous que chaque zone de texte est associée à un buffer, qui se remplit avec les caractères frappés au clavier.

Lorsque la variable est de type chaîne de caractères, la procédure de lecture ne fait que recopier ces caractères dans la variable.

S'il s'agit d'une variable de type Integer ou Double, il ne suffit pas de recopier le buffer dans la variable. Le contenu du buffer est tout d'abord codé en base 2, puis c'est le résultat de ce codage qui est stocké dans la variable.

Affichage des variables

L'affichage des trois variables se fait par le bouton Afficher, dont voici la procédure évènementielle :

 
Sélectionnez
procedure TForm1.BT_AfficherClick(Sender: TObject);
begin
  Afficher (Ville, ZT_Ville);
  AfficherEntier (CP, ZT_CodePostal);
  AfficherNombre (NH, ZT_Hab);
end;

Comme pour la lecture, il faut choisir la procédure d'affichage en fonction du type de la variable : Afficher pour afficher une variable de type String, AfficherEntier pour une variable de type integer et AfficherNombre pour une variable de type Double.

Dans les parenthèses, on précise le nom de la variable à afficher, puis le nom de la zone de texte dans laquelle on souhaite l'afficher.

La procédure Afficher recopie simplement le contenu de la variable dans le buffer, qui est ensuite affiché dans la zone de texte par le système d'exploitation.

Dans le cas des procédures AfficherNombre et AfficherEntier, un décodage préalable est nécessaire avant de recopier la chaîne de caractères représentant le nombre dans le buffer.

Effacement des zones de texte

L'effacement des zones de texte s'effectue avec le bouton Effacer, dont voici la procédure évènementielle :

 
Sélectionnez
procedure TForm1.BT_EffacerClick(Sender: TObject);
begin
  Effacer (ZT_Ville); Effacer (ZT_Hab);
  Effacer (ZT_CodePostal);
end;

Il suffit ici de préciser entre parenthèses le nom de la zone de texte à effacer.

Argumentation pédagogique

Cette section s'adresse au lecteur averti (professeur ou élève avancé) désirant comprendre la raison pour laquelle j'ai introduit les procédures de lecture et d'affichage.

Reprenons notre exemple. Voici comment lire les trois variables sans utiliser les procédures Lire, LireEntier et LireNombre :

 
Sélectionnez
procedure TForm1.BT_LireClick(Sender: TObject);
begin
  Ville := ZT_Ville.Text;
  CP := StrToInt(ZT_CodePostal.Text);
  NH := StrToFloat (ZT_Hab.Text);
end;

Ce qui me gène ici est l'utilisation obligatoire de la programmation objet. On accède à l'attribut Text de la classe TEdit. C'est encore pire lorsqu'il s'agit d'afficher du texte dans une zone de liste, puisqu'il faut alors appliquer la méthode Add à l'attribut items (objet de la classe TStringList) d'un objet de la classe TListBox.

Or, je pense que l'on peut difficilement commencer un cours de programmation avec des notions de programmation objet. À mon sens, la programmation objet ne peut être abordée qu'après avoir bien assimilé les types structurés (puisque les objets sont en quelque sorte des types structurés étendus) et les sous-programmes (puisque les méthodes sont des sous-programmes associés à des classes).

Mis à part la programmation objet, d'autres arguments peuvent être ajoutés en faveur de l'utilisation des procédures de lecture et d'affichage :

  • cela rend le code plus lisible ;
  • cela oblige les élèves à programmer plus proprement : les données doivent obligatoirement être transférées dans des variables avant d'être utilisées. On ne travaille pas directement sur les zones de texte pour faire des calculs ;
  • on voit mieux la différence entre une lecture (ou un affichage) et une simple affectation.

Les expressions

Vous connaissez déjà tous les expressions mathématiques dans lesquelles on retrouve des nombres, des opérateurs de calculs (+, -, etc) et des variables. Par exemple :

 
Sélectionnez
(a + b) × (a - 17)

Dans les langages de programmation, on retrouve également cette notion d'expression sous une forme un peu différente et plus générale.

Une expression est une écriture possédant une valeur et un type, dans laquelle figurent (entre autres) des littéraux, des constantes, des opérateurs et des noms de variables.

Vous savez déjà ce que sont les variables. Par contre, nous n'avons pas encore défini les littéraux, les constantes et les opérateurs.

La valeur d'une expression est le résultat du calcul de cette expression, en tenant compte des valeurs des variables qu'elle contient. Son type est le type du résultat.

L'écriture Age+1, est, par exemple, une expression de type entier, car :

  • la variable Age est de type entier ;
  • 1 est un entier ;
  • la somme de deux entiers est un entier.

Sa valeur est celle de la variable Age augmentée de 1.

Une expression peut faire intervenir plusieurs opérations. Par exemple :

 
Sélectionnez
  (Age + 1) * 2

représente le double de la valeur de la variable Age augmentée de 1.

Voilà différentes valeurs des expressions Age+1 et (Age+1)*2 pour différentes valeurs de la variable Age :

Age Age+1 (Age + 1) * 2
10 11 22
0 1 2
43 44 88

Les littéraux

Un littéral est l'écriture d'une valeur d'un certain type de variable.

L'écriture des littéraux obéit à un certain nombre de conventions qui dépendent du type et du langage de programmation.

Les littéraux numériques

Un littéral numérique représente la valeur d'un nombre.

Il existe de nombreuses manières d'écrire un nombre. On peut par exemple avoir :

  • une simple suite de chiffres. Cette écriture ne convient que pour les nombres entiers positifs. Exemple : 2012 ;
  • pour pouvoir représenter des entiers relatifs, il faut autoriser la présence d'un signe + ou - devant cette suite de chiffres. Exemple : -15, +320 ;
  • pour pouvoir représenter des nombres non entiers, on utilise le point. La partie entière du nombre est suivi d'un point puis d'une autre suite de chiffres. Exemples : 3.141559, -5.33333 ;
  • pour pouvoir représenter des nombres très grands ou très petits, on autorise également la notation scientifique. Exemple : 1.7E+308.

Les littéraux chaînes de caractères

Un littéral de type chaine de caractères s'écrit entre quotes.

Par exemple : 'Il fait beau', '4012', '(3 x + 1)', 'VisualC++', '@%^š€' sont des littéraux de type chaîne de caractères.

On remarquera que tous les caractères sont autorisés entre les quotes sauf les quotes eux mêmes !. Pour mettre une quote dans une chaîne de caractères, il faut la faire précéder par une autre quote. Par exemple :

 
Sélectionnez
    L''horaire d''été'

Parmi les chaînes de caractères, il y en a une qui joue un rôle particulier : la chaine vide.

La chaine vide est une chaine qui ne contient aucun caractère. On l'écrit '' (deux quotes).

Les Constantes

Une constante est un littéral portant un nom. Avant la compilation (ou l'interprétation) du code, ce nom sera remplacé par le littéral dans tout le code.

Une constante est donc en quelque sorte le synonyme d'une valeur. On pourra par exemple introduire une constante PI représentant le nombre 3.14159. Avant la compilation du code (cette étape s'appelle en fait la précompilation), toutes les occurrences du mot PI seront remplacées par 3.14159.

Comme son nom l'indique, la valeur d'une constante ne peut pas varier durant l'exécution d'un programme.

Déclaration d'une constante en Pascal

En Pascal, une constante peut se déclarer de la manière suivante :

 
Sélectionnez
    Const nom de constante = littéral ;

Le nom d'une constante obéit aux même règles que le nom d'une variable.

Exemple :

 
Sélectionnez
    Const PI = 3.14159 ;
     CLEF_USB = 'F:' ;  DISQUE_DUR = 'C:';

Opérateur

En informatique, un opérateur est généralement représenté par un ou deux caractères représentant une certaine opération.

Voici, par exemple, quelques opérateurs fréquemment rencontrés dans les langages de programmation : +   -  /  *  ^  =  ++  !   %

Dans ce cours, nous nous concentrerons pour l'instant sur les opérateurs suivants :

  • l'opérateur d'addition, représentée par le caractère ;
  • l'opérateur de soustraction,représenté par le caractère ;
  • l'opérateur de multiplication, représenté par le caractère * (et non pas × comme en mathématique !) ;
  • L'opérateur de division, représenté par le caractère ;
  • l'opérateur de concaténation, représenté par le caractère +.

Vous connaissez déjà les quatre premiers opérateurs, puisqu'il s'agit des opérateurs mathématiques classiques. Mais si vous n'avez jamais fait de programmation, vous ignorez probablement le sens de l'opérateur de concaténation.

La concaténation

Concaténer deux chaînes de caractères signifie les mettre bout à bout.

Par exemple, si je concatène la chaine 'para' avec la chaine 'pluie', j'obtiens la chaîne 'parapluie'.

La manière de noter l'opérateur de concaténation dépend des langages.

En Pascal, il se note comme l'opérateur d'addition, c'est à dire avec un signe +.

L'opérateur de concaténation permet de construire des expressions de type chaîne de caractères.

Par exemple, 'Je suis '+Prenom est une expression de type chaîne de caractères constituée d'un littéral ('Je suis '), de l'opérateur de concaténation + et d'un nom de variable (Prenom).

On peut concaténer autant de chaînes de caractères qu'on le souhaite. Par exemple, dans l'expression 'Je suis '+Prenom+' '+Nom il y a quatre chaînes de caractères.

Voilà différentes valeurs de l'expression

 
Sélectionnez
 'Je suis ' + Prenom

pour différentes valeurs de la variable Prenom :

Prenom 'Je suis '+Prenom
'Jimi' 'Je suis Jimi'
'Albert' 'Je suis Albert'

Voilà différentes valeurs de l'expression

 
Sélectionnez
 'Je suis ' + Prenom + ' ' + Nom

pour différentes valeurs des variables Prenom et Nom :

Prenom Nom 'Je suis '+Prenom+' '+Nom
'Jimi' 'Hendrix' 'Je suis Jimi Hendrix'
'Albert' 'Einstein' 'Je suis Albert Einstein'
'Albert' '' 'Je suis Albert'

Validité d'une expression

L'écriture d'une expression dans un certain langage de programmation doit nécessairement obéir aux règles propres à ce langage.

Une expression de type T est valide si elle peut être « comprise » par l'interpréteur (ou le compilateur) comme un calcul d'une valeur de type T ou plus généralement comme la construction d'un objet de type T.

Nous donnons ici, un certain nombre de règles non exhaustives permettant d'écrire des expressions valides en Pascal.

Expressions valides de type chaîne de caractères

Les règles suivantes permettent de construire des expressions de type chaîne de caractères valides :

Règle1 : un nom de variable est une expression valide si et seulement cette variable est de type chaîne de caractères.

Règle2 : un littéral (ou une constante représentant ce littéral) est une expression valide si et seulement si il est du type chaîne de caractères.

Règle3 : A+B est valide si et seulement si A et B sont deux expressions valides de type chaîne de caractères.

Règle4 : un littéral, une constante ou un nom de variable ne pas être suivi immédiatement d'un littéral, d'une constante ou d'un nom de variable.

Exemples d'application de ces règles (Nom et Prenom sont des chaînes de caractères et Age est un entier) :

Expression Validité Règle non respectée
Prenom Oui  
Age Non 1
'Thirion' Oui  
48 Non 2
'Eric'+'Thirion' Oui  
Prenom+Nom Oui  
Prenom+'Thirion' Oui  
'Cours de'+Prenom+'Thirion' Oui  
'Eric'+'Thirion a' +Age+' ans' Non 3
'Eric'+'Thirion a' + 48 +' ans' Non 3
Prenom Nom Non 4
'Eric' 'Thirion' Non 4
Prenom 'Thirion' Non 4

Expressions valides de type numérique

Les règles suivantes (non exhaustives) permettent de construire des expressions numériques valides :

Règle1 : une variable est une expression numérique valide si et seulement si elle est de type numérique (integer ou double par exemple).

Règle2 : un littéral (ou une constante représentant ce littéral) est une expression numérique valide si et seulement si il est de type numérique.

Règle3 : les écritures A+B, A-B, A*B, A/B sont des expressions numériques valides si et seulement si A et B sont des expressions numériques valides.

Règle4 : un littéral, une constante ou un nom de variable ne peut pas être suivi immédiatement d'un littéral, d'une constante ou d'un nom de variable.

Règle5 : (A) est une expression numérique valide si et seulement si A est une expression numérique valide.

Règle6 : chaque parenthèse ouvrante correspond à une et une seule parenthèse fermante.

Exemples d'application de ces règles (avec Nom de type String, Age de type Integer et Poids de type Double) :

Expression Validité Règle non respectée
Nom Non 1
Age Oui  
201 Oui  
20.14 Oui  
'20.14' Non 2
Age * Poids Oui  
Age + Nom Non 3
Poids/(Age*2.2) Oui  
Age Poids Non 4
Age 49 Non 4
(Age * Poids) Oui  
(Age * Nom) Non 5
(Poids/((Age*(2.2))) Oui  
Poids/((Age + 1 ) Non 6

Affichage d'expression

Les procédures d'affichage de l'unité ETBib permettent d'afficher la valeur d'une expression. Selon le type de l'expression, on utilisera Afficher, AfficherEntier ou AfficherNombre.

Expression de type chaîne de caractères

Pour afficher une expression E de type chaîne de caractères dans une zone de texte ou zone de liste Z, on utilisera l'instruction :

 
Sélectionnez
   Afficher ( E, Z);

Exemple : la variable Nom contient le nom d'une personne et la variable Adresse son adresse. L'instruction suivante permet d'afficher le nom et l'adresse de la personne sur une seule ligne dans la zone de liste ZL_Client :

 
Sélectionnez
   Afficher (Nom + '-' + Adresse, ZL_Client);

Le nom et l'adresse sont séparés par un tiret.

Expression numérique

Pour une expression numérique dont le résultat est toujours un nombre entier, on peut utiliser AfficherEntier ou AfficherNombre. Par contre, si le résultat n'est pas forcément entier, il faudra utiliser AfficherNombre.

Exemple 1 : Un hôtel propose des chambres à 47 euros. La variable NJ contient le nombre de jours. L'instruction suivante permet d'afficher le prix du séjour dans la zone de texte ZT_PrixSejour :

 
Sélectionnez
   AfficherEntier ( NJ * 47 , ZT_PrixSejour);

Dans cet exemple, on peut utiliser la procédure AfficherEntier car le prix à payer est forcément un nombre entier.

Exemple 2 : Supposons à présent que le prix d'une chambre soit variable et pas forcément entier. Ce prix est stocké dans une variable P de type double. Dans ce cas, on ne sait pas si le prix du séjour est un nombre entier. Il faut donc utiliser l'instruction :

 
Sélectionnez
   AfficherNombre ( NJ * P , ZT_PrixSejour);

Traitement sans variables-résultats

Revenons sur le schéma lecture-traitement-affichage. À présent, nous pouvons expliquer comment afficher les résultats d'un traitement sans les sauvegarder dans des variables : il suffit d'écrire l'expression du traitement à l'intérieur de l'instruction d'affichage !

Voilà par exemple comment écrire le programme d'addition sans sauvegarder le résultat dans une variable :

 
Sélectionnez
 var
  Form1: TForm1;
  // Déclaration des variables globales
  x, y  : integer;

implementation

{ TForm1 }

procedure TForm1.BoutonAdditionnerClick(Sender: TObject);
begin
  // Lecture des données
  LireEntier (x,zoneTexteX);
  LireEntier (y,ZoneTexteY);
  // Traitement et Affichage du résultat
  AfficherEntier (x + y, ZoneTexteSomme);

end;

Le calcul de l'addition se fait à l'intérieur de l'instruction d'affichage.

Dans des cas simples, cette manière de programmer peut être utilisée. Mais de manière générale, essayez autant que possible de séparer le traitements des données et l'affichage. Vous obtiendrez ainsi des programmes plus lisibles et plus faciles à modifier.

L'affectation

L'affectation est une instruction qui permet de modifier la valeur d'une variable. Plus précisément :

Affecter une valeur à une variable signifie enregistrer cette valeur dans la plage mémoire allouée à cette variable.

Affecter une valeur à une variable modifie donc l'état de la zone mémoire qui lui est allouée, ou autrement dit son « contenu ».

Reprenons notre exemple avec les quatre variables Age, NombreEnfant, Nom et Prenom. Supposons qu'à un instant donné l'état de la mémoire soit le suivant :

Image non disponible

Si on affecte ensuite la valeur 33 à la variable Age, on obtient :

Image non disponible

Et la zone mémoire allouée à la variable Age restera inchangée jusqu'à ce qu'une valeur différente lui soit affectée.

Écriture d'une instruction d'affectation en Pascal

En pascal, on représente l'affectation par l'opérateur :=. Le nom de la variable figure à gauche de := et la valeur qu'on veut lui affecter, à droite.

Par exemple, affecter la valeur 33 à la variable Age s'écrira :

 
Sélectionnez
   Age := 33 ;

De manière générale, ce qui figure à droite de := est une expression. Une affectation s'écrit donc sous la forme :

 
Sélectionnez
    Nom de variable := expression ;

Une affectation est toujours exécutée de droite à gauche. C'est-à-dire que l'expression figurant à droite du signe := est d'abord évaluée et que sa valeur est ensuite enregistrée dans la variable dont le nom figure à gauche du :=.

Exemple de code Pascal avec des affectations

Voilà un premier exemple de code Pascal, principalement composé d'affectations :

 
Sélectionnez
 Var Age, MonAge  : Integer;
 begin
 Age := 20 ;
 MonAge := 47 ; 
 Age := Age+1 ;
 Age := Age*2 ;
 MonAge := Age ;
 end;

Ce code commence par la déclaration de deux variables de type entier (Age et MonAge). Après la partie déclaration, on a cinq instructions d'affectation.

Voilà comment se déroule l'exécution de ce code :

Après l'exécution de l'instruction Age vaut MonAge vaut
Age := 20 ; 20 ?
MonAge := 47 ; 20 47
Age := Age + 1 ; 21 47
Age := 2 * Age ; 42 47
MonAge := Age ; 42 42

Remarque : la première valeur de la variable MonAge est indéterminée. Tant que l'on a pas affecté de valeur à une variable, on ne sait pas ce qu'elle contient.

Affectation récursive

Dans une affectation, l'expression affectée à la variable peut elle même contenir la variable. On dit alors que l'affectation est récursive.

Exemple 1 :

Ajouter le prix d'un article au prix total, se traduit par l'affectation récursive suivante :

 
Sélectionnez
 PrixTotal := PrixTotal + Prix ;

Exemple 2 :

Augmenter une variable de 1, se dit incrémenter. C'est un cas particulier d'affectation récursive très fréquemment utilisé en programmation.

Par exemple, pour incrémenter le nombre d'articles :

 
Sélectionnez
 NombreArticle := NombreArticle + 1 ;

Traitement itératif

Les affectations récursives sont en général utilisées dans des traitements itératifs, c'est-à-dire des traitements que l'on répète plusieurs fois. Chaque répétition d'un traitement itératif s'appelle une itération.

Illustrons ceci par un exemple.

Considérons un programme permettant de calculer la durée totale des morceaux d'un album. Voici son interface graphique :

Image non disponible

Le bouton Ajouter ajoute le nouveau titre saisi dans la zone de liste et augmente la durée totale de l'album par la durée de ce morceau.

Nous avons ici un exemple de traitement itératif, puisque les mêmes opérations vont être effectuées chaque fois que l'utilisateur ajoute un album. Ou autrement dit, chaque fois qu'il clique sur le bouton Ajouter.

Voici, le code de la procédure évènementielle de ce bouton :

 
Sélectionnez
procedure TForm1.BT_AjouterClick(Sender: TObject);
begin
  //--- Lecture des données
  Lire (Titre, ZT_Titre);
  LireEntier (Duree, ZT_Duree);

  //--- Traitement des données
  DureeT := DureeT + Duree;
  Numero := Numero + 1;

  //--- Affichage
  AfficherEntier (Numero, ZL_Num);
  Afficher (Titre, ZL_Titre);
  AfficherEntier (Duree, ZL_Duree);
  AfficherEntier (DureeT, ZT_DureeT);

end;

Le traitement consiste à :

  • lire le titre du morceau et sa durée ;
  • augmenter la durée totale (DureeT) par la durée du morceau ;
  • incrémenter le numéro du morceau ;
  • afficher le numéro, le titre et la durée du morceau dans les zones de liste prévues à cet effet ;
  • afficher la durée totale.

Mais que vaut la durée totale au départ ? Quel est le numéro du premier morceau ?

Cela dépend de la valeur de ces variables avant la première itération, c'est-à-dire au démarrage du programme.

Il faudrait donc pouvoir affecter des valeurs de départ (on dit initialiser) aux variables DureeT et Numero. Sinon, on ne pourra pas connaître leurs valeurs initiales.

Mais avec ce que nous avons vu jusqu'à présent, nous n'avons aucun moyen de faire exécuter des instructions à l'ordinateur avant le premier clic sur un bouton.

Heureusement, il existe une procédure évènementielle qui est appelée dès le démarrage du programme. Elle s'appelle Form_Create. Pour l'ajouter au code, il suffit de double-cliquer sur le fond du formulaire.

C'est donc dans la procédure Form_Create, qu'il faut initialiser les variables.

Voici la procédure Form_Create de notre exemple :

 
Sélectionnez
procedure TForm1.FormCreate(Sender: TObject);
begin
  //--- Initialisation
  DureeT := 0;
  Numero := 0;
end;

Comme cette procédure est déclenchée dès le démarrage du programme, nous saurons que la durée totale et le numéro seront nulles avant la première itération.

Exemple et Conseils

L'objectif de la programmation n'est pas seulement de faire des programmes qui marchent.

En réalité, un programme n'est jamais terminé (du moins un programme conséquent tel que vous en rencontrerez en entreprise). Il faut donc pouvoir le modifier facilement, sachant que le développeur qui sera chargé de la mise à jour de votre programme ne sera pas forcément vous-même. Cela a deux conséquences :

  1. votre code doit être lisible, c'est-à-dire qu'en le lisant on devine facilement ce qu'il fait. ;
  2. d'autre part, il doit être facilement modifiable.

Dans cette partie du cours, je vous donne à-travers un exemple quelques conseils permettant de rendre un programme plus lisible et plus facile à modifier.

Mise à part la lisibilité et la facilité de modification, il existe d'autres critères importants en programmation que nous n'aborderons pas ici :

  • la rapidité d'exécution du code. Cet aspect est le sujet essentiel de l'algorithmique, qui peut être vue comme la science de résoudre un problème par un programme de manière à ce que ce programme s'exécute le plus rapidement possible. Sachez simplement que la manière de programmer peut jouer énormément sur la vitesse d'exécution ;
  • la concision du code, c'est-à-dire la résolution d'un problème avec un programme le plus court possible. Certains programmeurs sont capable d'écrire en deux lignes ce que d'autres écrivent en vingt lignes. Cet aspect est à mon avis moins important que les autres dans la mesure où un code concis est souvent peu lisible.

Exemple de programme et conseil

Cahier des charges

La réalisation d'un projet informatique démarre en général avec un cahier des charges. C'est en gros une description de ce que devrait faire le logiciel.

Voici le cahier des charges de notre exemple :

On souhaiterait écrire un programme qui calcule la surface à peindre dans une pièce rectangulaire possédant des portes et des fenêtres.

Pour simplifier, on supposera que les murs ont 2m50 de hauteur et que toutes les fenêtres ont la même dimension (1.4 × 1.2) et de même pour les portes (0.9 × 2.1).

Déclaration des constantes

Introduisons une constante pour chaque donnée fixe du problème :

 
Sélectionnez
   const
    LargeurFenetre = 1.4;
    HauteurFenetre = 1.2;
    LargeurPorte = 0.9;
    HauteurPorte = 2.1;
    HauteurMur = 2.5;

On pourrait se passer de ces déclarations et mettre directement les valeurs 1.4, 1.2, etc dans les calculs, mais on obtiendrait ainsi un code moins lisible et plus difficile à mettre à jour.

S'il faut corriger la hauteur des portes par exemple, il suffit de changer la valeur de la constante et cette modification sera automatiquement répercutée dans tout le code (dans tous les calculs où la hauteur des portes intervient).

Essayez de procéder de cette manière dès que vous avez des valeurs fixées. Déclarez des constantes au lieu d'écrire les valeurs dans les instructions.

Conception de l'interface graphique

La surface à peindre dépendra donc de la taille de la pièce ainsi que du nombre de portes et de fenêtres. Ce seront les données du programme. On en déduit l'interface graphique suivante :

Image non disponible

Les zones de texte sont nommées ZT_Largeur (largeur de la pièce), ZT_Longueur (longueur de la pièce), ZT_NF (nombre de fenêtres), ZT_NP (nombre de portes) et ZT_SP (surface à peindre).

Les noms des zones de texte sont toutes préfixées par ZT_. Cette manière de procéder permet d'éviter des conflits avec des noms de variables. En programmation, un même nom ne peut désigner qu'une seule chose. Vous ne pouvez pas, par exemple, nommer une zone de texte Largeur et utiliser par ailleurs une variable de même nom. Je vous propose de préfixer les noms des zones de texte par ZT_, ceux des zones de liste par ZL_ et ceux des boutons par BT_. Vous pouvez bien sûr avoir vos propres conventions. L'essentiel est d'en avoir une.

Lecture et déclaration des données

Associons une variable à chaque donnée : LargeurPiece, LongueurPiece, NFenetre et NPorte. D'où les déclarations :

 
Sélectionnez
 Var LargeurPiece, LongueurPiece : Double; 
     NFenetre, NPorte : Integer;

Je n'ai pas besoin de vous expliquer ici la signification de chaque variable, car les noms choisis permettent de deviner la fonction de chacune d'entre elles. Pour obtenir un code lisible, il est recommandé de procéder de cette manière : utilisez des noms de variables parlants. Cette remarque est évidemment également valable pour les noms de constantes et pour, de manière générale, n'importe quel nom utilisé dans un programme.

Nous pouvons à présent écrire les instructions de lecture de données dans la procédure évènementielle du bouton Calculer :

 
Sélectionnez
procedure TForm1.BT_CalculClick(Sender: TObject);
begin
  // Lecture des données
  LireNombre (LargeurPiece, ZT_Largeur);
  LireNombre (LongueurPiece, ZT_Longueur);
  LireEntier (NFenetre, ZT_NF);
  LireEntier (NPorte, ZT_NP); 
end;

Traitement, résultats intermédiaires et finaux

Il nous faut maintenant réfléchir à la manière de calculer la surface à peindre. Pour simplifier le problème, nous allons le découper le traitement en plusieurs étapes :

  1. calcul de la surface des murs ;
  2. calcul de la surface des fenêtres ;
  3. calcul de la surface des portes ;
  4. calcul de la surface à peindre.

Le résultat de chaque étape sera sauvegardé dans une variable : SurfaceDesMurs, SurfaceDesFenetres, SurfaceDesPortes, SurfacePeinture.

Essayez également de procéder de cette manière lorsque vous avez un calcul compliqué. Découpez le calcul en plusieurs parties et sauvegardez le résultat de chacun de ces calculs dans une variable. À la fin, écrivez le calcul du résultat final en utilisant les résultats des calculs intermédiaires. Cette manière de programmer simplifie la résolution du problème et rend le programme plus lisible.

Il nous faut d'abord déclarer les nouvelles variables :

 
Sélectionnez
 Var LargeurPiece, LongueurPiece : Double; 
     NFenetre, NPorte : Integer;
     SurfaceDesMurs, SurfaceDesPortes, SurfaceDesFenetres,
     SurfacePeinture : double;

Vous pouvez également procéder de cette manière. Il est en général difficile de prévoir dès le début toutes les variables que l'on aura besoin d'utiliser dans un programme. Ajoutez vos déclarations de variable au fur et à mesure des besoins.

Écrivons à présent les quatre étapes de calcul.

Calcul de la surface des quatre murs :

 
Sélectionnez
 SurfaceDesMurs :=  
   2 * (LargeurPiece+LongueurPiece) * HauteurMur;

Calcul de la surface des fenêtres :

 
Sélectionnez
 SurfaceDesFenetres := 
  NFenetre * LargeurFenetre * HauteurFenetre;

Calcul de la surface des portes :

 
Sélectionnez
  SurfaceDesPortes := 
    NPorte * LargeurPorte * HauteurPorte;

Calcul de la surface à peindre :

 
Sélectionnez
 SurfacePeinture := 
   SurfaceDesMurs  - SurfaceDesFenetres - SurfaceDesPortes;

Le code de la procédure en entier

Pour conclure, voici le code de la procédure évènementielle du bouton Calculer :

 
Sélectionnez
procedure TForm1.BT_CalculClick(Sender: TObject);
begin
  //--- Lecture des données
  
  LireNombre (LargeurPiece, ZT_Largeur);
  LireNombre (LongueurPiece, ZT_Longueur);
  LireEntier (NFenetre, ZT_NF);
  LireEntier (NPorte, ZT_NP); 
  
  //--- Traitement des données

  SurfaceDesMurs :=  
   2 * (LargeurPiece+LongueurPiece) * HauteurMur; 

  SurfaceDesFenetres := 
  NFenetre * LargeurFenetre * HauteurFenetre; 

  SurfaceDesPortes := 
    NPorte * LargeurPorte * HauteurPorte;

  SurfacePeinture := 
   SurfaceDesMurs  - SurfaceDesFenetres - SurfaceDesPortes;

  // Affichage des résultats
  
  AfficherNombre (SurfacePeinture,ZT_SP);  
end;

Le code de cette procédure est bien séparé en trois parties : lecture des données, traitement des données, puis affichage des résultats. Essayez également de procéder de cette manière. Écrivez votre code en séparant bien la lecture, le traitement et l'affichage.

Synthèse

Voici un résumé de tous les conseils vus précédemment :

  • Lorsque vous avez des valeurs fixées, utilisez des constantes.
  • Préfixez les noms des composants de l'interface graphique (suggestion : ZT_ = zone de texte, ZL_ = zone de liste, BT_ = bouton).
  • Utilisez des noms parlants.
  • Lorsque vous avez un calcul (ou plus généralement un traitement) compliqué, découpez-le en plusieurs parties et sauvegardez les résultats de chaque partie dans des variables.
  • Séparez bien la lecture des données, le traitement des données et l'affichage des résultats.

Exercices

Retrouvez différents exercices sur le site de l'auteur :