Discipline de programmation, validation et test
Cours · informatique (MP2I/MPI), chapitre 4 · MP2I et MPI · le début du chapitre, la suite sur Adloun
4.1 Un chapitre qui ne se termine jamais
Les autres chapitres de ce livre s'ouvrent et se referment. Celui-ci s'ouvre et reste ouvert : le programme officiel marque ses deux sections S1 S2 S3-4, et précise qu'elles ont « vocation à être observée[s] dès le début et durant toute la durée des deux années d'enseignement ».
La raison est donnée sans détour : « les défauts, les bogues et les failles de logique constituent systématiquement la cause première des vulnérabilités des logiciels exploitées de façon malveillante ». Un programme faux n'est pas seulement un programme faux ; c'est une porte. Ce chapitre fonde donc des habitudes, et chaque ligne de code du livre les appliquera.
4.2 Spécifier avant d'écrire
La spécification d'une fonction dit, sans dire comment :
- sa signature : le type de chaque paramètre, le type du retour ;
- sa précondition : ce que l'appelant garantit sur les entrées ;
- sa postcondition : ce que la fonction garantit sur la sortie.
Le programme est catégorique : « les signatures des fonctions sont toujours précisées ».
/* Renvoie l'indice de la première occurrence de v dans les n premiers termes
de t, ou -1 si v n'y figure pas.
Précondition : n >= 0, et t a au moins n cases.
Postcondition : soit le résultat vaut -1 et v est absent de t[0..n-1],
soit 0 <= résultat < n et t[résultat] == v. */
int indice_de(const int t[], int n, int v);
(* indice_de t v renvoie Some i tel que t.(i) = v pour le plus petit tel i,
et None si v ne figure pas dans t.
Précondition : aucune. *)
val indice_de : 'a array -> 'a -> int option
Les deux versions décrivent le même contrat. Notez ce que le type OCaml dit tout seul : int option rend l'échec visible dans la signature, là où le de C doit être expliqué en français.
Bonne pratique (La spécification s'écrit avant le corps)
Une spécification écrite après coup décrit ce que le code fait ; une spécification écrite avant décide ce qu'il doit faire. La différence se voit à l'usage : la seconde attrape les cas limites — que renvoie-t-on sur un tableau vide ? sur une valeur en double ? — pendant qu'ils sont encore gratuits à traiter.
4.3 Annoter : précondition, postcondition, invariant
Le programme demande l'« annotation d'un bloc d'instructions par une précondition, une postcondition, une propriété invariante », et précise : « ces annotations se font à l'aide de commentaires ».
/* Trie les n premiers termes de t par ordre croissant, en place.
Précondition : n >= 0. */
void tri_selection(int t[], int n) {
for (int i = 0; i < n - 1; i = i + 1) {
/* INVARIANT : t[0..i-1] est trié, et tous ses termes sont
inférieurs ou égaux à ceux de t[i..n-1]. */
int mini = i;
for (int j = i + 1; j < n; j = j + 1) {
if (t[j] < t[mini]) { mini = j; }
}
int tmp = t[i]; t[i] = t[mini]; t[mini] = tmp;
}
}
Bonne pratique (Un commentaire ne paraphrase pas le code)
« Les parties complexes de codes ou d'algorithmes font l'objet de commentaires qui l'éclairent en évitant la paraphrase », dit le programme. La règle se vérifie en supprimant le code : si le commentaire ne dit plus rien d'utile, il ne disait rien.
i = i + 1; /* on incrémente i <- INUTILE : paraphrase */
i = i + 1; /* on passe au candidat suivant <- utile : l'INTENTION */
4.4 Programmation défensive
4.4.1 Assertions
assertUne assertion est une condition dont on affirme qu'elle est vraie à cet endroit. Si elle est fausse, le programme s'arrête immédiatement avec un message précis.
#include <assert.h>
assert(n >= 1 && t != NULL);
assert (Array.length t >= 1)
Le programme « encourage » leur usage « par exemple pour valider des entrées ou pour le contrôle de débordements ».
Sans assertion, une précondition violée produit un résultat faux qui se propage : on découvre le problème mille instructions plus loin, dans une fonction innocente. Avec elle, on l'apprend à l'endroit exact où le contrat a été rompu, et le message nomme le fichier et la ligne. Le temps gagné se compte en heures.
Exercices du chapitre
- Assertion, ou traitement d'erreur corrigé
- Le jeu de tests d'une division euclidienne corrigé
- Cent pour cent des instructions, quatre-vingt-trois pour cent des arcs corrigé
- Toutes les façons de satisfaire une condition corrigé
- Les chemins qui n'existent pas corrigé
- Problème — La postcondition qu'on oublie : trier, c'est aussi permuter corrigé