Skip to main content

Introduction à la programmation C# · Module 5, leçon 2 sur 3

Chercher et corriger méthodiquement

Remplacer les essais au hasard par une méthode : reproduire, localiser, comprendre, corriger, vérifier et documenter chaque erreur.

Résultat attendu

À la fin de la leçon, vous corrigez un programme qui contient plusieurs erreurs, une à la fois, et vous remettez un journal de correction qui prouve chaque correction.

Dans cette leçon
  1. L'essentiel
  2. Exercices
  3. Solutions
  4. Je fais le point

Ce que vous allez faire

  • Reconnaître les trois familles d'erreurs.
  • Lire un message de compilation et un message d'exception.
  • Appliquer la méthode en six étapes.
  • Tenir un journal de correction.

Vous aurez réussi si…

  • chaque correction répond à un cas qui échouait ;
  • vous ne changez qu'une chose à la fois ;
  • vos tests réussissent tous après la dernière correction.

L'essentiel

Devant un programme qui ne fonctionne pas, la tentation est de modifier une ligne « pour voir », puis une autre. On finit souvent avec plus d'erreurs qu'au départ, sans savoir laquelle des modifications a aidé. Un informaticien procède comme un enquêteur : il établit les faits, formule une hypothèse, la vérifie, puis consigne ce qu'il a trouvé.

Trois familles d'erreurs

CompileréchecErreur de compilationle compilateur refuse :code CS et numéro de ligneExécuteréchecErreur d'exécutionle programme plante(exception) ou se figeCompareréchecErreur de logiqueil se termine, mais lerésultat est faux
Plus une erreur apparaît tard, plus elle est difficile à trouver. Une erreur de compilation donne sa ligne ; une erreur de logique ne donne rien : seul un test qui compare à l'attendu la révèle.
FamilleSymptômePremier réflexe
CompilationLe programme ne démarre pas ; liste de messages CSLire le premier message : code, ligne, colonne. Les suivants en découlent souvent.
Exécution« Unhandled exception » ; ou le programme ne rend plus la mainLire le type d'exception et la ligne indiquée ; ou arrêter avec le débogueur (pause) pour voir où il tourne.
LogiqueRésultat faux, sans aucun messageTrouver le cas le plus simple qui échoue, puis suivre les variables au débogueur.

Lire un message d'exception

Une exception s'affiche en anglais, en quelques lignes. Trois informations suffisent : le type, le message et la ligne.

Message d'exception
Unhandled exception. System.DivideByZeroException: Attempted to divide by zero.
   at Program.<Main>$(String[] args) in C:\Cours\Pas\Program.cs:line 14

Ici : une division entière par zéro, à la ligne 14 de Program.cs. Les exceptions les plus fréquentes à ce stade sont FormatException (un Parse sur un texte qui n'est pas un nombre) et DivideByZeroException (une division entière par zéro).

La méthode en six étapes

1Reproduireun cas qui échoue2Localiseroù l'écart apparaît3Comprendrela cause, prouvée4Corrigerune chose à la fois5Vérifierce cas et les autres6Documenterle journalencore faux ?
Les étapes 2 et 3 sont le cœur du travail : on ne corrige que ce qu'on a compris. Si la vérification échoue, on retourne localiser, au lieu d'empiler les modifications.
  • Reproduire : trouver des entrées précises qui font échouer le programme à tout coup. « Ça marche pas » n'est pas un cas ; « 2 trajets, 120 km et 80 km, donnent 1 571,43  au lieu de 7,70  » en est un.
  • Localiser : repérer la première valeur qui s'écarte de l'attendu. Pour un long programme, on divise pour régner : on vérifie une valeur au milieu du traitement ; si elle est juste, l'erreur est après, sinon elle est avant.
  • Comprendre : formuler la cause en une phrase et la prouver par une valeur observée. Expliquer le code à voix haute, ligne par ligne, même à un objet inanimé (la méthode du canard en caoutchouc), révèle souvent l'erreur.
  • Corriger : faire une seule modification, la plus petite possible.
  • Vérifier : rejouer le cas qui échouait, puis tous les autres tests (non-régression).
  • Documenter : consigner l'erreur dans le journal de correction.

Exemple complet : le covoiturage

Le programme doit calculer le coût de l'essence partagé entre les passagers d'un covoiturage, pour plusieurs trajets. La voiture consomme 7 L aux 100 km, et l'essence coûte 1,65  le litre. Cas de test : 2 trajets de 120 km et 80 km, 3 personnes. Résultat attendu : 14,0 L, 23,10 , soit 7,70  par personne.

C# · Program.cs (version reçue)
const double Consommation = 7;     // litres aux 100 km
const double PrixLitre = 1.65;

Console.Write("Nombre de trajets : ");
int nbTrajets = int.Parse(Console.ReadLine() ?? "");
Console.Write("Nombre de personnes : ");
int nbPersonnes = int.Parse(Console.ReadLine() ?? "");

double totalKm = 0;
int trajet = 1;
while (trajet <= nbTrajets)
{
    Console.Write($"Distance du trajet {trajet} (km) : ");
    double km = double.Parse(Console.ReadLine() ?? "")
    totalKm += km;
}

double litres = totalKm * 100 / Consommation;
double cout = litres * PrixLitre;
double parPersonne = cout / nbPersonnes;
Console.WriteLine($"Essence : {litres:F1} L, {cout:C}");
Console.WriteLine($"Par personne : {parPersonne:C}");

Le programme contient trois erreurs, une de chaque famille. On les corrige dans l'ordre où elles se présentent :

  1. Compilation : le premier message est CS1002 : ; attendu, ligne 14. Il manque le point-virgule à la fin de la lecture de km.
  2. Exécution : une fois le programme compilé, il redemande sans fin « Distance du trajet 1 ». Au débogueur, trajet vaut toujours 1 : la progression trajet++; manque dans la boucle.
  3. Logique : le programme se termine, mais affiche 2857,1 L et 1 571,43  par personne. Un point d'arrêt après le calcul de litres montre 2857,14 alors qu'on attend 14 : la formule est inversée. Les litres valent totalKm * Consommation / 100.
NoSymptômeCasCauseCorrectionVérification
1CS1002, ligne 14CompilationPoint-virgule manquantAjout du ;Compile sans erreur
2Demande sans fin « trajet 1 »2 trajetstrajet reste à 1 : pas de progressionAjout de trajet++; à la fin du blocDemande les trajets 1 et 2, puis s'arrête
32857,1 L au lieu de 14,0 L120 et 80 km, 3 personnesFormule des litres inverséetotalKm * Consommation / 10014,0 L ; 23,10  ; 7,70  ; et 1 trajet de 50 km pour 2 personnes : 2,89 
Pourquoi une correction à la fois

Si on change trois lignes d'un coup et que le résultat devient juste, on ne sait pas lesquelles étaient nécessaires ; si le résultat reste faux, on ne sait pas si une modification a empiré les choses. Une modification, puis un test : chaque pas est une preuve.

Quatre pièges fréquents
  • Corriger le deuxième message de compilation en premier : il est souvent une conséquence du premier. Corrigez le premier, puis recompilez.
  • Modifier le test pour qu'il passe : si le résultat attendu est juste, c'est le programme qu'on corrige.
  • S'arrêter au premier cas réussi : une correction peut en briser une autre partie. Rejouez tous les tests.
  • Laisser des traces : les Console.WriteLine ajoutés pour enquêter doivent être retirés à la fin.

Exercices

GuidéExercice 1 : Classer les symptômes

Pour chaque symptôme, donnez la famille d'erreur (compilation, exécution ou logique) et votre premier réflexe.

  1. CS0103 : Le nom 'totl' n'existe pas dans le contexte actuel, ligne 12.
  2. System.FormatException: The input string 'douze' was not in a correct format.
  3. La facture affiche un total dix fois trop grand.
  4. Le programme n'affiche plus rien et ne rend jamais la main.
  5. CS0165 : Utilisation d'une variable locale non assignée 'moyenne'.
  6. Le maximum affiché est 0 pour une semaine de températures négatives.

Vous avez réussi si vous classez deux erreurs dans chaque famille, et si chaque réflexe nomme un outil précis (message, ligne, débogueur, cas de test).

GuidéExercice 2 : Lire une exception

Ce programme calcule la moyenne de pas par jour. Avec la seule saisie 0, il affiche le message d'exception présenté plus haut.

C# · Program.cs
int total = 0;
int nbValeurs = 0;
int valeur = -1;
while (valeur != 0)
{
    Console.Write("Nombre de pas (0 pour terminer) : ");
    valeur = int.Parse(Console.ReadLine() ?? "");
    if (valeur != 0)
    {
        total += valeur;
        nbValeurs++;
    }
}
int moyenne = total / nbValeurs;
Console.WriteLine($"Moyenne : {moyenne} pas par jour");
  1. Nommez le type d'exception et la ligne en cause.
  2. Expliquez la cause en une phrase.
  3. Corrigez le programme pour qu'il affiche « Aucune donnée. » quand aucune valeur n'est saisie.

Vous avez réussi si la saisie 0 affiche « Aucune donnée. », et si 8000, 6000, 0 affiche toujours « Moyenne : 7000 pas par jour ».

Semi-guidéExercice 3 : Le remboursement d'un prêt

Ce programme doit compter les versements mensuels nécessaires pour rembourser un prêt sans intérêt, et afficher le dernier versement, qui peut être plus petit. Il contient deux erreurs.

C# · Program.cs (avec erreurs)
Console.Write("Montant emprunté : ");
double montant = double.Parse(Console.ReadLine() ?? "");
Console.Write("Versement mensuel : ");
double versement = double.Parse(Console.ReadLine() ?? "");

double solde = montant;
int mois = 1;
double dernier = 0;
while (solde >= 0)
{
    if (solde < versement)
    {
        dernier = solde;
    }
    else
    {
        dernier = versement;
    }
    solde -= dernier;
    mois++;
}
Console.WriteLine($"Nombre de versements : {mois}");
Console.WriteLine($"Dernier versement : {dernier:C}");

Appliquez la méthode et tenez le journal de correction (six colonnes). Utilisez ces cas : 1000  et 150  ; 900  et 150  ; 100  et 150 .

Vous avez réussi si votre journal compte deux lignes, et si le programme corrigé donne 7 versements (dernier : 100,00 ), 6 versements (dernier : 150,00 ) et 1 versement (dernier : 100,00 ).

AutonomeExercice 4 : Diviser pour régner

Cette facture applique un rabais de 10 % à partir de 100  d'achat, puis les taxes. Pour 4 articles à 30 , on attend 124,17 , mais elle affiche 13,80 . Avec 2 articles à 25 , elle donne le bon total, 57,49 .

C# · Program.cs (avec erreur)
const double Rabais = 0.10;
const double SeuilRabais = 100;
const double TauxTps = 0.05;
const double TauxTvq = 0.09975;

Console.Write("Prix unitaire : ");
double prix = double.Parse(Console.ReadLine() ?? "");
Console.Write("Quantité : ");
int quantite = int.Parse(Console.ReadLine() ?? "");

double sousTotal = prix * quantite;
if (sousTotal >= SeuilRabais)
{
    sousTotal = sousTotal * Rabais;
}
double tps = sousTotal * TauxTps;
double tvq = sousTotal * TauxTvq;
double total = sousTotal + tps + tvq;
Console.WriteLine($"Total : {total:C}");

Localisez l'erreur en vérifiant d'abord une seule valeur, au milieu du traitement : le sous-total après le rabais. Rédigez la ligne du journal.

Vous avez réussi si vous expliquez pourquoi le cas à 50  ne révélait pas l'erreur, et si 4 × 30  donne 124,17  et 1 × 100  donne 103,48 .

DéfiExercice 5 : L'erreur qui se cache

Ce programme réussit les tests 2, 7, 10 et 97 : il dit que 2, 7 et 97 sont premiers, et que 10 ne l'est pas. Pourtant, il contient une erreur.

C# · Program.cs
Console.Write("Nombre entier (2 ou plus) : ");
int n = int.Parse(Console.ReadLine() ?? "");

bool estPremier = true;
for (int diviseur = 2; diviseur * diviseur < n; diviseur++)
{
    if (n % diviseur == 0)
    {
        estPremier = false;
    }
}

if (estPremier)
{
    Console.WriteLine($"{n} est premier.");
}
else
{
    Console.WriteLine($"{n} n'est pas premier.");
}

Aucun symptôme n'est fourni : c'est à vous de trouver un cas qui échoue. Raisonnez sur la condition de la boucle pour choisir vos tests, puis corrigez et documentez.

Vous avez réussi si vous trouvez au moins deux nombres mal classés, si vous expliquez ce qu'ils ont en commun, et si la version corrigée les classe bien tout en réussissant les quatre tests d'origine.

Solutions

Exercice 1 : Classer les symptômes
  1. Compilation : aller à la ligne 12 ; une faute de frappe dans un nom de variable.
  2. Exécution : un Parse a reçu « douze » ; chercher la lecture en cause et la protéger avec TryParse.
  3. Logique : trouver le plus petit cas qui échoue, puis vérifier les valeurs intermédiaires au débogueur (un facteur 10, souvent un taux mal écrit).
  4. Exécution : une boucle infinie probable ; mettre le débogueur en pause pour voir la ligne où il tourne et la variable qui ne progresse pas.
  5. Compilation : une branche n'affecte pas moyenne ; vérifier que chaque chemin lui donne une valeur.
  6. Logique : le maximum est initialisé à 0 ; il doit partir de la première valeur (leçon 4.3).
Exercice 2 : Lire une exception
  1. DivideByZeroException, ligne 14 : int moyenne = total / nbValeurs;.
  2. Avec 0 comme seule saisie, nbValeurs vaut 0, et une division entière par zéro est impossible.
  3. Ajouter un test avant la division :
C# · Fin du programme corrigée
if (nbValeurs == 0)
{
    Console.WriteLine("Aucune donnée.");
}
else
{
    int moyenne = total / nbValeurs;
    Console.WriteLine($"Moyenne : {moyenne} pas par jour");
}

Avec des double, 0 / 0 ne plante pas, mais donne NaN : l'erreur deviendrait une erreur de logique, plus discrète. Le test est donc nécessaire dans les deux cas.

Exercice 3 : Le remboursement d'un prêt
NoSymptômeCasCauseCorrectionVérification
1Le programme se fige après les saisies1000 et 150Quand solde atteint 0, solde >= 0 reste vrai et dernier vaut 0 : le solde ne bouge pluswhile (solde > 0)Le programme se termine, mais affiche 8 versements
28 versements au lieu de 71000 et 150mois part de 1 : un versement de trop comptéint mois = 0;7 (100,00 ) ; 6 (150,00 ) ; 1 (100,00 )

La deuxième erreur n'était visible qu'une fois la première corrigée : c'est typique. Le cas 900  et 150  est la limite où le dernier versement est complet.

Exercice 4 : Diviser pour régner

Un point d'arrêt sur la ligne double tps = … montre sousTotal = 12 au lieu de 108 : l'erreur est avant, et la seule ligne qui modifie le sous-total est celle du rabais. sousTotal * Rabais calcule le montant du rabais (10 %), pas le prix après rabais (90 %). Il faut sousTotal * (1 - Rabais).

Le cas à 50  ne passe pas dans le if : il ne peut pas révéler une erreur qui se trouve dans le if. Un bon plan de tests exécute chaque branche au moins une fois.

Ligne du journal : sous-total de 12  au lieu de 108  ; cas 4 × 30  ; rabais calculé comme un montant ; sousTotal * (1 - Rabais) ; vérifié avec 124,17 , 103,48  et 57,49 .

Exercice 5 : L'erreur qui se cache

La condition diviseur * diviseur < n exclut le cas où le diviseur au carré vaut exactement n. Les carrés de nombres premiers sont donc mal classés : 4, 9, 25, 49 et 121 sont déclarés premiers. Pour 9, la boucle s'arrête avant de tester 3, puisque 3 × 3 < 9 est faux.

Correction : diviseur * diviseur <= n. Vérification : 4, 9, 25 et 49 ne sont plus premiers ; 2, 7 et 97 le sont toujours ; 10 ne l'est toujours pas.

Les tests d'origine avaient tous des nombres « faciles ». Chercher les valeurs à la frontière d'une condition, ici l'égalité, est la méthode des valeurs limites de la leçon 2.3.

Je fais le point

  • Je sais reconnaître une erreur de compilation, d'exécution ou de logique.
  • Je sais lire le premier message du compilateur et un message d'exception.
  • Je sais reproduire une erreur avec un cas précis avant de la corriger.
  • Je sais localiser une erreur en divisant le traitement en deux.
  • Je sais ne faire qu'une correction à la fois, puis rejouer tous les tests.
  • Je sais tenir un journal de correction.

Rich Text

This is my rich text.

I am a subheading

This is more rich text.

  • I am a list

  • Lists are cool

world's best human
Breakdance is flexible, powerful, and easy-to-use. It's everything I need to build a website.
Louis Reingold
CEO @ Breakdance