Résumé
Cette leçon ouvre la première unité du cours, consacrée aux premiers pas dans la programmation avec C++. Elle explique à quoi sert l’unité : mener qui n’a jamais programmé d’un problème énoncé en langage courant jusqu’à un programme qui compile, s’exécute, lit des données et écrit des résultats, en sachant ce que chaque outil a fait en chemin ; elle présente ses six leçons et le fil qui les unit, depuis l’algorithme écrit en français jusqu’à la classification des erreurs ; elle précise le peu qui est tenu pour acquis, à savoir l’arithmétique scolaire, l’habitude de suivre des instructions écrites et le maniement des fichiers et des dossiers ; elle formule le problème que l’unité résout, celui de convertir un calcul que l’on sait faire à la main en une procédure qu’une machine exécute sans se tromper et sans se fatiguer ; et elle montre où elle mène, dans les unités suivantes et dans le cours Programmation en C++ II.
Objectifs d’apprentissage
- Décrire la séquence des contenus de l’unité et expliquer ce que chaque leçon apporte à la suivante.
- Reconnaître les connaissances préalables que le cours suppose (opérer avec des entiers et des décimaux, calculer un quotient et un reste, suivre des instructions écrites, enregistrer un fichier dans un dossier déterminé) et les réviser si nécessaire.
- Expliquer, avec un exemple, le chemin que l’unité apprend à parcourir : de l’énoncé à la spécification, de la spécification à l’algorithme et de l’algorithme au programme que l’on compile et que l’on exécute.
- Reconnaître ce que l’on attend de savoir faire à la fin de l’unité, et le distinguer de ce que l’on apprendra dans les unités suivantes.
Évaluation d’entrée
Avant de parcourir l’unité, il convient de mesurer le point de départ. Cette évaluation est une autoévaluation : elle n’est pas notée, et son seul but est d’orienter l’étude. Comme il s’agit de la première unité du cours, il n’y a pas de questions sur des unités précédentes ; à leur place, les trois premières vérifient ce que le cours tient pour acquis : le quotient et le reste d’une division entière, l’ordre des étapes d’une procédure, et le nom, l’extension et le dossier d’un fichier. Les sept suivantes touchent, dans l’ordre, aux idées centrales des six leçons de l’unité. Qui n’a jamais programmé ne doit pas s’attendre à les réussir, et peu importe : chaque réponse, juste ou non, explique le point et nomme la leçon qui le traite. Les mêmes compétences sont mesurées de nouveau à la fin, dans la leçon de synthèse.
Avant de commencer l’unité, mesurez votre point de départ. C’est une autoévaluation brève : elle n’est pas notée et son résultat n’est conservé que dans votre navigateur.
Question 1Des unités précédentesOn répartit 47 feuilles entre 6 personnes, en parts entières égales, et celles qui ne suffisent pas pour un tour complet sont mises de côté. Combien chaque personne en reçoit-elle et combien en met-on de côté ?
C’est la division avec décimales, non la division entière : les feuilles ne se découpent pas, et la question demande combien il en reste.Arrondir le quotient vers le haut exige des feuilles qui n’existent pas ; le reste de la division entière n’est jamais négatif lorsque le dividende et le diviseur sont strictement positifs.Elle confond le reste avec la partie décimale du quotient : le reste se compte en feuilles, non en fractions de tour.C’est la division entière : le quotient est le plus grand nombre de tours complets, et le reste ce qui reste, toujours inférieur au diviseur. Le cours l’utilise dès le premier programme, et en C++ elle a ses propres opérateurs.À revoir : Du problème à l’algorithme
Question 2Des unités précédentesPour envoyer un fichier joint par courrier électronique, il y a quatre étapes : A, écrire l’adresse du destinataire ; B, cliquer sur « Envoyer » ; C, ouvrir un nouveau message ; D, joindre le fichier. Quel ordre est le bon ?
L’ordre de l’énumération n’est pas l’ordre de l’exécution : avec lui, le message serait envoyé avant d’exister.Le résultat dépend de l’ordre : ce que l’on joint après l’envoi n’arrive pas.Elle envoie le message sans destinataire et sans pièce jointe ; les étapes suivantes ne le changent plus.Certaines étapes dépendent d’autres (on ne peut pas joindre un fichier à un message qui n’a pas été ouvert, ni compléter un message déjà envoyé), et d’autres non. Reconnaître ces dépendances est la première chose qu’exige l’écriture d’un algorithme.À revoir : Du problème à l’algorithme
Question 3Des unités précédentesUn fichier est enregistré dans
C:\Users\ana\programas\suma.cpp. Quel est son nom, quelle est son extension et dans quel dossier se trouve-t-il ?Elle confond le dossier qui contient le fichier avec le fichier lui-même : le nom est le dernier composant du chemin.L’extension fait partie du nom ; certains systèmes la cachent par défaut, et le cours demande de l’afficher, parce quesuma.cppetsuma.exesont des fichiers très différents.C:est le lecteur où commence le chemin, non une extension.Le chemin énumère les dossiers depuis la racine jusqu’au fichier ; l’extension, ce qui suit le dernier point du nom, dit quelle sorte de contenu il a :.cppest du code source C++. Pour le compiler, le terminal doit se placer dans ce dossier ou recevoir le chemin complet.À revoir : Compiler, exécuter et configurer
Question 4Une recette dit : « Si la pâte est sèche, ajouter une cuillerée d’eau et pétrir ; répéter jusqu’à ce qu’elle ne le soit plus ». La pâte a besoin d’exactement trois cuillerées pour cesser d’être sèche. Combien de fois demande-t-on si la pâte est sèche ?
Elle ne compte pas la question dont la réponse est « non », qui est posée elle aussi : sans elle, personne ne saurait qu’il faut cesser d’ajouter de l’eau.Elle confond le texte de la procédure avec son exécution : la condition est écrite une fois, mais elle est évaluée à chaque tour.Elle compte les intervalles entre les cuillerées, non les questions.Dans toute répétition qui pose la question avant d’agir, la condition est évaluée une fois de plus que le corps : la dernière question est celle qui permet de sortir. Suivre la procédure à la main, en comptant, le montre sans ambiguïté.À revoir : Du problème à l’algorithme
Question 5Laquelle de ces instructions toute personne, ou une machine, peut-elle suivre exactement de la même manière ?
Elle dépend du goût de qui l’exécute : deux personnes la suivraient de manière différente, et une machine n’a pas de goût.Elle ne dit ni combien de fois ni ce qu’est être bien : la condition d’arrêt n’est pas vérifiable.Elle ne fixe pas les nombres : « grand » n’est pas une quantité, et le diviseur pourrait être zéro.Elle dit quelle opération on fait, avec quelle donnée et où se trouve le résultat ; rien ne dépend du jugement de qui l’exécute. Cette précision est l’une des trois exigences d’un algorithme.À revoir : Du problème à l’algorithme
Question 6Un programme se compile sans erreurs ni avertissements, s’exécute bien avec beaucoup de données, et avec l’une en particulier s’arrête avec le message « division by zero ». À quel moment l’erreur s’est-elle produite ?
Le compilateur traduit le texte, il ne l’exécute pas, et il ne connaît pas les données que l’utilisateur tapera.L’éditeur de liens réunit les parties du programme ; il ne fait pas de calculs et ne voit pas de données.Avec d’autres données, le même exécutable fonctionne : le défaut est dans ce que le programme fait de cette donnée, non dans les outils.Ce qui dépend seulement du texte du programme se découvre à la compilation ou à l’édition de liens ; ce qui dépend des données, seulement à l’exécution. C’est pourquoi un programme qui compile n’est pas, pour autant, exempt d’erreurs.À revoir : Programmes, compilateurs et C++
Question 7On a installé Visual Studio Code avec son extension C++, et lorsqu’on demande
g++ --versiondans le terminal, celui-ci répond qu’il ne reconnaît pasg++. Qu’est-ce qui est le plus probable ?L’extension ne contient pas le compilateur et n’intervient pas dans ce que le terminal trouve.La commandeg++ --versionne compile aucun programme : elle demande seulement la version, et le terminal n’a même pas trouvé à qui la demander.L’éditeur est indifférent : ce qui manque, c’est le compilateur, dans n’importe quel éditeur.L’éditeur ne compile pas : il appelle un compilateur à part, qui doit être installé et trouvé par le terminal. L’extension C++ aide à écrire le code, mais n’apporte pas de compilateur.À revoir : Installer l’environnement sous Windows, macOS et Linux
Question 8Le compilateur écrit un message qui commence par
warning:et, malgré lui, produit l’exécutable. Qu’est-ce que cela signifie ?Ce serait une erreur (error:), et il n’y aurait alors pas d’exécutable.Fonctionner avec les données testées ne prouve rien sur les autres ; un avertissement ignoré est souvent l’erreur de demain.L’exécutable est normal ; ce qui est douteux, c’est ce que le programme fait à l’endroit signalé.Une erreur empêche d’obtenir l’exécutable ; un avertissement le permet, mais signale du code suspect. Beaucoup d’avertissements révèlent des défauts réels, et c’est pourquoi le cours exige des programmes qui compilent sans aucun.À revoir : Compiler, exécuter et configurer
Question 9Où commence l’exécution d’un programme C++ ?
La première ligne est d’ordinaire une directive#include, qui est traitée avant la compilation et ne s’exécute pas.L’ordre dans le fichier ne décide pas du point d’entrée ; c’est le nommainqui en décide.La norme fixe le nom du point d’entrée ; il ne se choisit pas dans la commande de compilation.mainest le point d’entrée : le système l’appelle au lancement du programme, et le programme se termine lorsqu’elle se termine. Un programme sansmaincompile, mais l’édition de liens échoue.À revoir : Anatomie d’un programme
Question 10Un programme déclare
int age{};, exécutestd::cin >> age;pour lire un âge, et l’utilisateur tapeabc. Que se passe-t-il ?Il ne s’arrête pas : il continue comme si la lecture s’était bien passée, et c’est pourquoi le défaut est dangereux.Une variableintne garde que des entiers ; les lettres ne sont ni converties en nombre ni gardées.La lecture ne réessaie pas : elle échoue une fois et laisse le flux en erreur.La lecture d’un entier échoue en rencontrant une lettre ; la norme dit que la variable reçoit 0 et que les lectures suivantes ne feront rien tant que l’erreur n’aura pas été traitée. Si le programme ne vérifie pas la lecture, il écrit des conclusions fausses sans aucun signal.À revoir : Entrée, sortie et erreurs
Les contenus de l’unité
L’unité compte six leçons, qui parcourent dans l’ordre le chemin d’un problème à un programme qui fonctionne. Chacune suppose la précédente, et la première ne suppose rien du langage.
- Du problème à l’algorithme. Le cours commence sans code, parce que l’algorithme précède le programme. La leçon apprend à spécifier un problème par son entrée, sa sortie et la relation entre les deux (la précondition et la postcondition) ; à décrire un algorithme en étapes numérotées, sans ambiguïté ; à le suivre à la main avec un tableau de suivi ; à y reconnaître la séquence, la décision et la répétition ; et à justifier qu’une répétition se termine, avec une quantité qui décroît à chaque tour.
- Programmes, compilateurs et C++. Un algorithme écrit pour une machine est un programme, et un programme C++ ne s’exécute pas tel qu’il est écrit : le préprocesseur, le compilateur et l’éditeur de liens le convertissent en exécutable. La leçon parcourt ces trois étapes, distingue ce que l’on découvre à la compilation de ce qui ne se manifeste qu’à l’exécution, compare le modèle compilé avec le modèle interprété et situe dans le temps les normes du langage, jusqu’à C++23, celle que le cours utilise, et C++26, dont l’implémentation dans les compilateurs est en cours.
- Installer l’environnement sous Windows, macOS et Linux. Trois guides complets, un par système, pour installer un compilateur capable de C++23 et l’éditeur Visual Studio Code avec son extension C++, vérifier que tout fonctionne avec un programme de test et résoudre les défaillances fréquentes : le compilateur que le terminal ne trouve pas, la version ancienne et la norme qui n’a pas été demandée.
- Compiler, exécuter et configurer. Le cycle consistant à éditer, enregistrer, compiler, exécuter et lire, depuis le terminal et depuis l’éditeur, avec la commande de compilation du cours. La leçon distingue les erreurs, qui empêchent l’exécutable, des avertissements, qui signalent du code suspect ; elle active les détecteurs (celui des erreurs de mémoire, AddressSanitizer, et celui du comportement indéfini, UndefinedBehaviorSanitizer), qui surveillent le programme pendant qu’il tourne ; et elle explique pourquoi une sortie montre des symboles étranges au lieu des accents et du ñ.
- Anatomie d’un programme. Le programme minimal, pièce par pièce : la directive d’inclusion et les en-têtes, la fonction
mainet le code de sortie, l’espace de nomsstd, les instructions, les blocs et les commentaires. La leçon apprend à corriger les erreurs de structure en lisant les messages du compilateur depuis le premier, et fixe le guide de style du cours. - Entrée, sortie et erreurs. Le programme communique avec l’extérieur : il écrit avec
std::printetstd::println, avec des spécifications de format pour la largeur et les décimales, et lit avecstd::cin. La leçon montre ce qui se passe lorsque l’entrée n’a pas la forme attendue et comment on vérifie la lecture, et clôt l’unité avec la classification des erreurs selon leur moment : de compilation, d’édition de liens, d’exécution et de logique.
Le fil qui les unit est celui du programme lui-même. La première leçon le conçoit, la deuxième explique ce qu’il devient, la troisième installe les outils qui le transforment, la quatrième apprend à s’en servir et à écouter ce qu’ils disent, la cinquième le dissèque et la sixième le fait dialoguer avec qui l’utilise. S’il en est ainsi, à la fin de l’unité l’étudiant sait encore très peu de C++, mais il sait comment on travaille avec lui, et c’est ce que le reste du cours suppose.
Ce qui est tenu pour acquis
Le cours ne suppose aucune expérience préalable en programmation. Il suppose, en revanche, trois compétences qu’il convient de réviser maintenant, si l’évaluation d’entrée a révélé quelque doute.
La première est l’arithmétique scolaire : opérer avec des entiers et avec des décimaux, respecter l’ordre des opérations (la multiplication et la division avant l’addition et la soustraction, sauf parenthèses) et, surtout, calculer le quotient et le reste d’une division entière. Cette dernière est plus importante qu’il n’y paraît. Diviser 47 feuilles entre 6 personnes donne 7 à chacune et il en reste 5, ce qui s’écrit , avec le reste inférieur au diviseur . C++ a un opérateur pour le quotient et un autre pour le reste, et une grande partie des calculs sur les entiers (convertir des secondes en heures, séparer les chiffres d’un nombre, décider si un nombre est pair) se ramène à eux. Qui veut l’étudier en détail la trouve, avec sa démonstration, dans le cours Algèbre et géométrie I : nombres, fonctions et géométrie plane ; ici, il suffit de savoir la calculer.
La deuxième est l’habitude de suivre des instructions écrites à la lettre et de reconnaître quand une instruction ne suffit pas pour savoir quoi faire. Il ne s’agit pas d’une connaissance, mais d’une attitude : lire « ajouter du sel selon le goût » et remarquer que cette instruction dépend de qui l’exécute. La première leçon de l’unité convertit cette attitude en méthode.
La troisième est le maniement de base d’un ordinateur : créer un dossier, y enregistrer un fichier sous un nom choisi, reconnaître l’extension du nom (.cpp, .txt, .exe) et savoir dans quel dossier se trouve chaque fichier. Il convient de configurer dès maintenant le système pour qu’il affiche les extensions, que certains systèmes, comme Windows, cachent par défaut : le code source suma.cpp et l’exécutable suma.exe sont des fichiers très différents, et les confondre est une source d’erreurs absurdes. L’unité apprend aussi à utiliser le terminal, de sorte qu’il n’est pas nécessaire de savoir le faire d’avance ; il suffit d’être disposé à taper des commandes au lieu de cliquer sur des boutons. Installer des programmes exige, enfin, de pouvoir le faire sur son propre ordinateur ; la leçon d’installation indique une solution en ligne pour qui ne le peut pas.
L’anglais n’est pas supposé. Les messages du compilateur sont en anglais, et le cours apprend à les lire champ par champ, de sorte que ce qui importe en eux (le fichier, la ligne, la nature du défaut) se comprend sans maîtriser la langue. On ne suppose pas non plus de mathématiques au-delà de celles de l’école : les exemples utilisent la division, les moyennes, les tables de multiplication, les dates et les heures.
Le problème que l’unité résout
Un calcul que l’on sait faire à la main n’est pas encore un programme. La distance entre les deux est ce que l’unité apprend à parcourir, et un problème simple permet de la mesurer.
Un chronomètre enregistre la durée de chaque activité comme un nombre de secondes, par exemple 7384, et l’on veut l’écrire sous la forme habituelle d’heures, de minutes et de secondes, avec les minutes et les secondes toujours sur deux chiffres : 2:03:04. À la main, le calcul est une chaîne de divisions : , d’où résultent 2 heures et 184 secondes ; , d’où résultent 3 minutes et 4 secondes. Pour une durée, cela se fait en une minute. Pour les mille durées d’un relevé, en revanche, le calcul à la main est long et sujet aux erreurs, et la tâche se prête à ce qu’une machine l’exécute.
Or une machine ne peut pas recevoir l’explication précédente. Elle reçoit un texte dans un langage de programmation, et pour écrire ce texte il faut avoir résolu auparavant trois questions que le calcul à la main laissait implicites. La première est la spécification : ce qui entre (un nombre entier de secondes, positif ou nul), ce qui sort (trois entiers, les heures, les minutes et les secondes) et quelle relation les unit : si est le total, , avec et . La deuxième est l’algorithme : les étapes précises qui, pour toute entrée admissible, produisent cette sortie.
- prend la valeur du quotient de par .
- prend la valeur du quotient par du reste de par .
- prend la valeur du reste de par .
- Écrire , deux-points, sur deux chiffres, deux-points et sur deux chiffres.
La troisième est l’écriture de l’algorithme en C++, sous la forme exacte que le langage exige, et sa traduction en exécutable avec les outils adéquats. Le programme suivant le fait. Il contient beaucoup de choses qui n’ont pas encore été expliquées : les variables (leçon 2.1), les opérateurs / et %, qui donnent le quotient et le reste de la division entière (leçon 3.1), la lecture avec std::cin et les spécifications de format (leçon 1.6) ; la spécification {:02} de ce programme, qui écrit un nombre sur deux positions en complétant avec des zéros à gauche, est une variante de la largeur qu’enseigne cette leçon et qu’utilisera la leçon 3.1. Pour l’instant, il suffit d’y reconnaître les quatre étapes de l’algorithme.
#include <iostream>#include <print>int main(){ int total{}; // les variables seront expliquées dans la leçon 2.1 std::print("Durée en secondes : "); std::cin >> total; const int hours{total / 3600}; const int minutes{total % 3600 / 60}; const int seconds{total % 60}; std::println("{} s = {}:{:02}:{:02}", total, hours, minutes, seconds);}7384Durée en secondes : 7384 s = 2:03:04Les lignes 9 à 11 sont les étapes 1 à 3, et la ligne 12 est l’étape 4. Que le résultat coïncide avec celui calculé à la main n’est pas un hasard, mais la vérification que tout programme mérite : le tester d’abord avec une donnée dont on connaît la réponse.
Le programme résout le problème, mais l’unité apprend aussi à s’en méfier. Si l’utilisateur tape abc au lieu d’un nombre, la lecture échoue, total reste à zéro et le programme écrit, sans aucun signal d’alarme, que la durée est 0:00:00 : une conclusion fausse qui a l’apparence d’une vraie. S’il tape une durée strictement négative, la spécification ne dit pas ce qui doit se passer, et le résultat n’a pas de sens. Si l’on écrit total / 360 au lieu de total / 3600, le programme compile, s’exécute et donne un résultat erroné, et aucun outil ne le signale. Chacun de ces défauts appartient à une nature d’erreur différente, qui se découvre à un moment différent et se cherche d’une manière différente ; la dernière leçon de l’unité les classe. Il s’ensuit que le problème que l’unité résout n’est pas seulement celui d’écrire ce programme, mais celui de savoir pourquoi il fonctionne, avec quelles données il cesse de fonctionner et quel outil avertira, ou non, de chaque défaut.
Par conséquent, à la fin de l’unité l’étudiant pourra écrire, compiler et exécuter des programmes de ce genre : quelques données d’entrée, quelques calculs en séquence et quelques résultats écrits avec mise en forme. Il ne pourra pas encore prendre de décisions ni répéter des instructions en C++ (même s’il le peut dans un algorithme écrit en français), ni choisir avec discernement le type de chaque donnée ; c’est ce qu’apportent les unités suivantes.
Où elle mène
L’unité est le seuil du cours. Ce qu’elle utilise sans l’expliquer est expliqué ensuite, dans cet ordre. L’unité 2, « Données et types », explique les variables et la façon dont elles sont gardées en mémoire, les limites des entiers (et pourquoi un calcul correct peut déborder), l’arrondi des nombres décimaux et les conversions entre types. L’unité 3, « Expressions et opérateurs », explique l’arithmétique de C++, avec le quotient et le reste des nombres négatifs, la logique des conditions et ce que la norme laisse indéfini dans une expression. L’unité 4, « Structures de contrôle », porte en C++ les décisions et les répétitions que la première leçon écrit en français, et apprend à justifier qu’une boucle est correcte et se termine. L’unité 5 décompose les programmes en fonctions ; la 6 introduit les données composées (tableaux de taille fixe, vecteurs, tableaux à deux dimensions et enregistrements), avec les algorithmes de recherche et de tri et leur coût ; la 7 traite du texte, de l’entrée robuste et des fichiers ; et la 8, des programmes en plusieurs fichiers, des tests systématiques et du débogueur.
Après le cours, Programmation en C++ II poursuit avec ce que celui-ci laisse de côté à dessein : les pointeurs et la mémoire dynamique, les classes avec méthodes, les modèles propres, les fonctions anonymes et les exceptions. Aucun de ces sujets n’est nécessaire pour résoudre les problèmes de ce cours, et les reporter permet de se concentrer sur ce qui vaut dans tout langage : spécifier, concevoir l’algorithme, l’écrire avec clarté et vérifier qu’il est correct. La leçon de synthèse de cette unité recueillera précisément cette distinction, entre ce qui est propre à C++ et ce que l’on emportera dans tout autre langage.