Le débogueur intégré à Visual Studio est un outil puissant qui vous sera très utile tout au long de votre carrière. Il permet de voir ce qui se passe dans notre programme, une étape à la fois, en examinant le contenu des variables pendant l'exécution. C'est idéal pour résoudre des bogues étranges qu'on n'arrive pas à s'expliquer en regardant simplement le résultat de l'exécution.
Un point d'arrêt (breakpoint) est une ligne dans le code où vous voulez arrêter l'exécution normale du programme pour commencer à déboguer. Vous ne pourrez rien déboguer si vous n'avez pas au moins placé un point d'arrêt. On place un point d'arrêt simplement en cliquant dans la marge de gauche de notre code.
Il est possible de placer autant de points d'arrêt que désiré. On peut évidemment les enlever si nécessaire, simplement en cliquant dessus.
Normalement, nous utilisons par défaut l'exécution sans débogage (Ctrl-F5), puisque ce mode fait une pause une fois l'exécution terminée, ce qui nous permet de voir le contenu de la console avant qu'elle soit fermée.
Si on veut utiliser les points d'arrêt, il nous faudra toutefois utiliser "Démarrer le débogage" (F5), sinon ils seront ignorés. On peut donc laisser les points d'arrêt en place et choisir de les utiliser ou non.
L'idée de mettre un point d'arrêt est de pouvoir ensuite suivre l'exécution de votre programme un pas à la fois. Pour ce faire, on appuie sur F5 (démarrer le débogage) et le programme s'exécute normalement jusqu'à ce qu'il atteigne le premier point d'arrêt. Ensuite, l'exécution est suspendue, comme mise à pause. Votre code revient à l'avant-plan et une flèche jaune apparaît sur le point d'arrêt -- la ligne de code est également surlignée:
La flèche jaune indique la ligne qui sera la prochaine à être exécutée. L'exécution est donc suspendue juste avant l'exécution de la ligne où se trouve le point d'arrêt. Vous pouvez alors:
Notez qu'il existe des icônes dans la barre de boutons qui permettent aussi de réaliser ces tâches:
Plutôt que de devoir constamment passer sa souris sur une variable pour la voir changer, on peut utiliser la fenêtre d'espions pour qu'elle soit affichée pour nous en permanence. La fenêtre d'espions apparaît normalement en dessous de la fenêtre du code pendant qu'on est en débogage. Si ce n'est pas le cas, on peut la faire apparaître en faisant Déboguer -> Fenêtre -> Espions -> Espion 1 (notez qu'on peut en ouvrir 4 différentes, toutes équivalentes).
On ajoute un espion en:
On peut y mettre:
Notez que les expressions n'ont pas besoin d'être des expressions directement tirées du code. Les variables, par contre, doivent être visibles au point du code où on est rendu en ce moment (pensez à la portée des variables locales).
Lorsqu'une valeur vient de changer (au dernier pas), elle devient rouge. Lorsqu'elle est pareille à ce qu'elle était au pas précédent, elle est noire.
En cliquant sur un point d'arrêt avec le bouton de droite, on peut choisir "Condition" et définir une condition pour que le point d'arrêt s'active. Tant que la condition n'est pas vraie, le point d'arrêt est ignoré. Idéal pour s'arrêter à un moment précis d'une boucle!
On peut placer dans la boîte n'importe quelle expression relationnelle, qui peut utiliser des variables visibles à cet endroit du code. À chaque passage au point d'arrêt, la condition sera évaluée. Si elle est fausse, le point sera ignoré et l'exécution continuera. Si elle est vraie, le point d'arrêt s'activera et l'exécution sera suspendue.
Optionnellement, on peut également inscrire uniquement le nom d'une variable dans la case "Condition" et cocher "a changé". Le point d'arrêt s'activera si la variable donnée n'a pas la même valeur que lors du dernier passage au point d'arrêt.
Un point d'arrêt conditionnel a l'air de ça:
Une assertion est la vérification d'une condition essentielle à la bonne marche du programme, vérification qui ne sera faite que pendant la phase de développement et pas pendant l'utilisation régulière du programme terminé.
L'idée est de valider une certaine condition sans laquelle rien ne peut fonctionner et tout arrêter si jamais la condition est fausse. Mais attention! On ne doit pas utiliser les assertions pour valider les intrants fournis par l'usager, puisque les assertions seront ignorées lorsque le programme sera entièrement terminé. On validera plutôt l'état de variables internes, dont nous sommes seuls responsables du contenu, afin de nous aider à identifier les situations problématiques pendant que l'on débogue et qu'on teste notre programme.
Une assertion, en C++, s'écrit simplement comme ceci:
assert(condition);
La condition peut être une expression relative ou une expression logique, qui peut être réduite à true ou false, exactement comme la condition qu'on placerait dans un if. Par exemple:
assert(Compteur < 100);
Ou:
assert(Diviseur != 0);
Au moment où la ligne est exécutée, la condition est vérifiée. Si elle est fausse, le programme s'arrête immédiatement et affichera un message d'erreur correspondant à la condition:
Assertion failed: Diviseur != 0
N'oubliez pas d'inclure la bibliothèque assert.h si vous désirez utiliser les assertions:
#include <assert.h>
Depuis le début, nous compilons toujours en mode Debug, simplement parce que c'est l'option par défaut. Il y a des différences importantes entre les deux modes:
Debug:
Release:
On utilise normalement le mode Release une fois que le débogage est entièrement terminé, que les tests sont satisfaisants et que l'on s'apprête à remettre le programme aux utilisateurs. Comme les assertions sont ignorées, on peut simplement les laisser là et compiler en mode Release comme si de rien n'était.
On change le mode simplement en utilisant la liste déroulable au centre de la barre d'outils:
1- Lorsque le code suivant est exécuté, au moment où i atteint la valeur -850, que valent les variables j, k et x? Expliquez comment vous pouvez trouver la réponse aisément à l'aide du débogueur, et donnez les valeurs des variables.
#include <iostream> using namespace std; int main() { int j = 2; int k = 0; int x = 2000; for (int i = -9000; i < 950; i = i + j) { k = i * 2; while (x > i) { if (j % 2 == 0) { x = x - j; i--; } else if (j % 3 == 0) { x = x + j; i++; } else { x++; } k *= 2; } } }
2- Voici une solution à l'exercice 3.9 du cours d'algorithmique. En bonus, elle calcule la moyenne du groupe, en plus de la moyenne de chaque étudiant. Malheureusement, elle ne fonctionne pas très bien. Saurez-vous trouver les 11 erreurs qui s'y sont glissées? Aidez-vous du débogueur pour voir ce qui se passe! (et vous pouvez diminuer le nombre d'étudiants dans la classe pour fins de tests!)
Identifiez chaque erreur par un commentaire.
#include <iostream> using namespace std; int main() { float Note1, Note2, Note3; float Moyenne = 0; float MoyenneGroupe = 0; float SommeGroupe = 0; const float Passage = 0.60f; const float Bien = 0.70f; const float TresBien = 0.85f; const float NoteMin = 0.0f; const float NoteMax = 100.0f; const int NbEtudiants = 18; const int NbExam = 3; int Compteur = 1; while (Compteur <= NbEtudiants) { Compteur = Compteur + 1; cout << "Entrez la note 1 de l'etudiant " << endl; cin >> Note1; while (Note1 < NoteMin && Note1 > NoteMax) { cout << "Note invalide, veuillez entrer une note entre 0 et 100: "; cin >> Note1; } cout << "Entrez la note 2 de l'etudiant " << endl; cin >> Note1; while (Note1 < NoteMin && Note1 > NoteMax) { cout << "Note invalide, veuillez entrer une note entre 0 et 100: "; cin >> Note2; } cout << "Entrez la note 3 de l'etudiant " << endl; cin >> Note3; while (NoteMin < Note3 || Note3 > NoteMax) { cout << "Note invalide, veuillez entrer une note entre 0 et 100: "; cin >> Note3; } SommeGroupe = SommeGroupe + Moyenne; Moyenne = (Note1 + Note2 + Note3) / NbEtudiants; cout << "Moyenne de l'etudiant: " << Moyenne << ". "; if (Moyenne < Passage) { cout << "Echec!" << endl; } if (Moyenne < Bien) { cout << "Bien!" << endl; } if (Moyenne < TresBien) { cout << "Tres bien!" << endl; } else { cout << "Formidable!" << endl; } } MoyenneGroupe = SommeGroupe / Compteur; cout << endl << "Moyenne du groupe: " << MoyenneGroupe << endl; return 0; }
3- Ce programme est censé trouver le plus grand commun diviseur de deux nombres donnés par l'usager. On suppose que l'usager ne donne que des nombres entiers positifs (pas nécessaire de le valider). Malheureusement, plusieurs des tests fournis dans le jeu de tests échouent et son créateur vous demande de l'aider à déboguer.
Utilisez le débogueur pour tenter d'identifier tous les problèmes. Corrigez-les, mais identifiez avec un commentaire ce que vous avez changé et pourquoi afin que le créateur du programme puisse apprendre.
// Programme trouvant le plus grand commun diviseur (PGCD) à deux nombres // Il tente d'être le plus efficace possible // Le plus grand commun diviseur à deux nombres est le plus grand nombre qui divise // les deux nombres sans reste tous les deux. // // Jeu de tests: // Intrants Extrants // Nombre1 Nombre2 PGCD // 97 54 Aucun // 114 42 6 // 100 200 100 // 200 100 100 // 98 35 7 #include <iostream> using namespace std; int main() { int Nombre1; int Nombre2; int PGCD; // Plus grand commun diviseur de Nombre1 et Nombre2 int PlusPetit; // Le plus petit des deux nombres int Compteur; cin >> Nombre1; cin >> Nombre2; if (Nombre1 > Nombre2 && Nombre1 % Nombre2 == 0) { PGCD = Nombre1; } else if (Nombre2 > Nombre1 && Nombre2 % Nombre1 == 0) { PGCD = Nombre1; } else if (Nombre1 == Nombre2) { PGCD = Nombre1; } else { if (Nombre1 < Nombre2) { PlusPetit = Nombre1; } else { PlusPetit = Nombre2; } Compteur = 2; while (Compteur <= PlusPetit / 2) { if (Nombre1 % Compteur == 0 || Nombre2 % Compteur == 0) { PGCD = Compteur; } } } if (PGCD == 1) { cout << "Aucun diviseur commun a ces nombres" << endl; } else { cout << "Le plus grand commun diviseur a ces nombres est " << PGCD << endl; } }