Les bases de la programmation

Premières notions de programmation

Auteurs

| Eric Thirion | Alcatîz |

- Titre : Les bases de la programmation

- Auteurs : Eric Thirion, Alcatîz

- Parution : 24 novembre 2013

- Public visé :

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

Sommaire

- Un peu d'histoire

- L'ordinateur

- Codage binaire

- Le bit

- Le transistor

- L'octet et autres unités

- Le codage des nombres entiers en base 2

- Le codage du texte en ASCII

- Mémoire vive et adresse

- Langage machine et programme exécutable

- Processus

- Les entrées-sorties

- Les interruptions

- Les Interface Graphiques

- Historique

- Interface du système

- Le curseur

- Gestion du clic

- Fenêtre d'une application

- Les évènements

- Les zones de texte

- La programmation

- Notion de programme

- Notion de langage

- Fichiers sources

- Compilation

- Notion de compilateur

- Exemple de compilation

- Interprétation

- Les EDIs

- La programmation évènementielle

- Lazarus

- Notion de formulaire

- Génération automatique de code

- Programmation évènementielle

- Ordre d'exécution des instructions

- Notice d'utilisation

- Les variables

- La notion de variable

- Les noms de variables en Pascal

- La notion de type

- Déclaration d'une variable

- Déclaration d'une variable en Pascal

- Syntaxe générale

- Déclaration d'un entier

- Le type double

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

- Regroupement des déclarations

- Où faut-il déclarer les variables ?

- Lecture et affichage

- Données et résultats d'un traitement

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

- Pour un ordinateur

- Exemple du programme d'addition

- Historique

- La procédure writeln

- La procédure readln

- ETBib

- Exemple d'utilisation des procédures de ETBib

- Lecture des variables

- Affichage des variables

- Effacement des zones de texte

- Argumentation pédagogique

- Les expressions

- Les littéraux

- Les littéraux numériques

- Les littéraux chaînes de caractères

- Les Constantes

- Déclaration d'une constante en Pascal

- Opérateur

- La concaténation

- Validité d'une expression

- Expressions valides de type chaîne de caractères

- Expressions valides de type numérique

- Affichage d'expression

- Expression de type chaîne de caractères

- Expression numérique

- Traitement sans variables-résultats

- L'affectation

- Écriture d'une instruction d'affectation en Pascal

- Exemple de code Pascal avec des affectations

- Affectation récursive

- Traitement itératif

- Exemple et Conseils

- Exemple de programme et conseil

- Cahier des charges

- Déclaration des constantes

- Conception de l'interface graphique

- Lecture et déclaration des données

- Traitement, résultats intermédiaires et finaux

- Le code de la procédure en entier

- Synthèse

- Exercices

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 : té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 :

Et voici le un :

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.

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 :

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 :

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

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 :

Langage machine et programme exécutable

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.

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.

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 :

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).

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.

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 ?

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.

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.

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.

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 :

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).

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.

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.

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 :

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.

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.

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.

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

Voici par exemple une instruction écrite en pascal :

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 :

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

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 :

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.

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  :

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 :

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

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 :

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.

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.

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

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 :

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

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.

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).

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 :

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.

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 :

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 :

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.

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 :

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.

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.

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 :

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

La notion de type

Il existe différents types de variables.

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 :

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

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.

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 :

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 :

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 :

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 :

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 :

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.

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.

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.

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.

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

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

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 :

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) :

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 :

writeln (Somme);

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

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 :

readln (X);

où 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 :

Lecture des variables

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

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 :

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 :

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 :

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 :

(a + b) &#215; (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.

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 :

(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

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 :

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.

Les Constantes

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 :

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 :

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

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

'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

'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.

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 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 :

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

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 :

Age := 33 ;

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

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 :

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

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 :

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 :

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 :

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 :

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 :

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 :

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).

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 :

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 :

Var LargeurPiece, LongueurPiece : Double;
NFenetre, NPorte : Integer;

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

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.

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

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

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

Calcul de la surface des quatre murs :

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

Calcul de la surface des fenêtres :

SurfaceDesFenetres :=
NFenetre * LargeurFenetre * HauteurFenetre;

Calcul de la surface des portes :

SurfaceDesPortes :=
NPorte * LargeurPorte * HauteurPorte;

Calcul de la surface à peindre :

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 :

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;

Synthèse

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

Exercices

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

- recherches complémentaires sur internet ;

- prise en main de Lazarus ;

- programme sans interface graphique ;

- exercices de programmation ;

- exercices complémentaires ;

- exercices théoriques :

- - énoncé ;

- - corrigé.