Informatique et Sciences du Numérique au lycée : un pas plus loin/LANGAGES/Éléments de syntaxe et de sémantique/Éléments de base/Instructions et procédures

Un livre de Wikilivres.
Sauter à la navigation Sauter à la recherche

L'observation des effets et résultats d'un programme se fait par la réalisation d'effets de bord : il faut pouvoir dialoguer avec l'utilisateur et il faut de toute façon faire connaître le résultat du programme, soit en le faisant afficher sur un écran ou une imprimante, soit en l'envoyant au périphérique destinataire, par exemple un thermostat de réglage, une électrovanne, etc.

Est aussi considéré comme un effet de bord la modification du contenu d'une cellule de la mémoire centrale. Une telle cellule peut être dénotée dans un texte de programme par un identificateur, qui est donc lié à l'adresse-mémoire de cette cellule et cet identificateur est dit référencer la valeur contenue dans cette cellule. La liaison de cet identificateur à cette adresse-mémoire est construite par la déclaration de cet identificateur et ne peut pas être modifiée. En revanche, la valeur référencée par cet identificateur est modifiable par l'opération d'affectation ou une opération dérivée. La valeur liée à l'identificateur est donc dite mutable. On appelle pointeur un identificateur qui référence une adresse-mémoire. Les adresses-mémoire peuvent donc être directement manipulées à l'aide de pointeurs ou plus généralement d'expressions dénotant une adresse-mémoire. Voir aussi la page wikipedia Pointeurs (à relire).

Pour rendre compte des évolutions de la mémoire, il faut ajouter à la notion d'environnement, celle d'état de la mémoire. Un état de la mémoire est un ensemble de couples (adresse-mémoire, valeur à l'adresse) dépendant de l'environnement considéré. Toute adresse-mémoire figurant dans un état doit être atteignable à partir d'une liaison (identificateur, valeur mutable) présente dans l'environnement.

int n = 2;  

int* p = &n; 

*p = 5;

Dans cet exemple, l'instruction int n = 2; déclare l'identificateur n et son type et le définit en lui affectant la valeur 2. Sont donc construits la liaison (n, adresse_de_n) et l'état (adresse_de_n, 2). L'instruction int* p = &n; déclare l'identificateur p et son type pointeur d'entier et le définit en lui affectant la valeur adresse_de_n. La liaison (p, adresse_de_p) est ajoutée à l'environnement et le couple (adresse_de_p, adresse_de_n) est ajouté à l'état. L'instruction *p = 5; déréférence p, c'est-à-dire

1- va chercher la valeur liée à l'identificateur p dans l'environnement donc adresse_de_p,

2- va chercher dans l'état la valeur stockée à adresse_de_p donc adresse_de_n

3- remplace 2 stocké à adresse_de_n par 5.

Cet exemple montre la création d'un alias, c'est-à-dire de deux chemins pour accéder à une même adresse-mémoire.


Le statut de l'affectation et son utilisation dépendent beaucoup du style de programmation utilisé. Dans les langages impératifs comme C, l'affectation et les manipulations de pointeurs sont les opérations majeures. Une exécution peut être vue comme une suite de changements d'états, conduite par des instructions de contrôle (séquence, branchements, boucles, ... ), le passage d'un état au suivant résultant d'une affectation ou d'une instruction dérivée. Comme le montre l'exemple ci-dessus, suivre l'exécution d'un texte impératif, même court et concis, peut demander rapidement l'appui d'un modèle sémantique (pour aller plus loin, voir la partie Sémantiques).

Avec d'autres familles de langages, il est tout à fait possible d'écrire des programmes sans utiliser l'affectation, ou plus exactement, en n'en utilisant que des formes dédiées consistant en des écritures sur des périphériques. Cela permet entre autres de diminuer certaines vulnérabilités dûes à l'aliasing et ce, étant donné la puissance actuelle des ordinateurs, sans porter atteinte à l'efficacité (voir LAFOSEC - référence à inclure).

L'exécution des fonctions présentées dans la section Expressions, fonctions et valeurs n'effectue pas d'effet de bord. Ce sont des fonctions dites pures, c'est à dire des fonctions au sens mathématique. Leur définition les lie à une valeur fonctionnelle, qui a été appelée fermeture et comme mentionné dans la section Déclarations, cette liaison reste inchangée durant tout le temps de son existence, c'est-à-dire de sa portée. De telles fonctions pures ont la propriété dite de transparence référentielle : une expression est dite référentiellement transparente si sa valeur ne dépend pas de l'état-mémoire dans lequel elle est évaluée.

Cependant, dans la plupart des langages de programmation, les constructions syntaxiques appelées fonctions ( function ou method) peuvent effectuer des effets de bord et, en particulier modifier des variables par affectation. Prenons l'exemple caricatural de cette fonction qui modifie une variable globale C.

function f(x) = ( C := C+x)

C:= 1

C:= C+f(3)

A la fin de l'exécution de ce fragment de code, la valeur de C est soit 8 soit 5, selon l'ordre d'évaluation des opérandes de l'addition. La fonction f ne respecte pas la propriété de transparence référentielle. La propriété de commutativité de l'addition est perdue. Le résultat dépend du choix de l'ordre des opérandes des opérateurs binaires, choix qui est fait par le compilateur et qui peut parfois varier entre différentes versions des compilateurs d'un même langage. De plus, la lecture critique du code s'en trouve singulièrement compliquée.

De telles fonctions, effectuant des effets de bord et donc des transformations d'état, seront appelées si nécessaire fonctions à effets de bord pour les distinguer des fonctions pures.


La plupart des langages proposent la notion de procédure encore appelées sous-programmes, routines, méthodes, etc. Par exemple, en C, elles sont introduites par le mot-clé function, en Python par le mot-clé method, etc. Une procédure permet de nommer une suite d'instructions en faisant, comme pour une fonction, l'abstraction d'un ou de plusieurs identificateurs dans cette suite, qui définit son corps. Une fois définie, une procédure devient une instruction du langage qui est appelée, c'est à dire appliquée à des paramètres effectifs et exécutée.

Les fonctions à effets de bord ne diffèrent des procédures que par le fait qu'elles retournent une valeur et apparaissent donc dans des expressions. En effet, une fonction peut être vue comme un opérateur. Cet opérateur, appliqué à des arguments, donne comme résultat une valeur. La définition de cet opérateur fait certes intervenir des instructions qui peuvent modifier l'état courant pendant leur exécution mais cette modification ne peut se produire que par l'affectation dans le corps de la fonction des variables libres de cette fonction. En revanche, Le résultat de l' exécution d'une procédure est une modification de l'état courant, parfois explicitement décrite par des informations qui spécifient le mode de communication entre la procédure et l'état présent au moment de l'appel. La communication peut être décrite soit en donnant le mode de passage de ces paramètres dans la déclaration de la procédure, parfois appelée en-tête soit déduite de la relecture du corps de la procédure.

Application d'une fonction pure, d'une fonction à effet de bord et d'une procédure


voir Présentation de C