Aller au contenu
Topos Uranos

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

  1. Décrire la séquence des contenus de l’unité et expliquer ce que chaque leçon apporte à la suivante.
  2. 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.
  3. 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.
  4. 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.

Évaluation d’entrée

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.

  1. 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é ?

  2. 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 ?

  3. 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 ?

  4. 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 ?

  5. Question 5Laquelle de ces instructions toute personne, ou une machine, peut-elle suivre exactement de la même manière ?

  6. 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 ?

  7. Question 7On a installé Visual Studio Code avec son extension C++, et lorsqu’on demande g++ --version dans le terminal, celui-ci répond qu’il ne reconnaît pas g++. Qu’est-ce qui est le plus probable ?

  8. Question 8Le compilateur écrit un message qui commence par warning: et, malgré lui, produit l’exécutable. Qu’est-ce que cela signifie ?

  9. Question 9Où commence l’exécution d’un programme C++ ?

  10. Question 10Un programme déclare int age{};, exécute std::cin >> age; pour lire un âge, et l’utilisateur tape abc. Que se passe-t-il ?

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 main et le code de sortie, l’espace de noms std, 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::print et std::println, avec des spécifications de format pour la largeur et les décimales, et lit avec std::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 47=6⋅7+547 = 6 \cdot 7 + 5, avec le reste 55 inférieur au diviseur 66. 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.

ExempleUne durée en heures, minutes et secondes

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 : 7384=3600⋅2+1847384 = 3600 \cdot 2 + 184, d’où résultent 2 heures et 184 secondes ; 184=60⋅3+4184 = 60 \cdot 3 + 4, 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 tt est le total, t=3600h+60m+st = 3600 h + 60 m + s, avec 0≤m<600 \leq m < 60 et 0≤s<600 \leq s < 60. La deuxième est l’algorithme : les étapes précises qui, pour toute entrée admissible, produisent cette sortie.

  1. hh prend la valeur du quotient de tt par 36003600.
  2. mm prend la valeur du quotient par 6060 du reste de tt par 36003600.
  3. ss prend la valeur du reste de tt par 6060.
  4. Écrire hh, deux-points, mm sur deux chiffres, deux-points et ss 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.

Programme en C++
#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);}
Entrée du programme
7384
Sortie
Durée en secondes : 7384 s = 2:03:04

Les 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.