Aller au contenu principal

Décider et répéter

Ce que ce chapitre apporte5 points
  • Écrire une condition et enchaîner des cas avec if, else if, else.
  • Distinguer = de ==, et reconnaître la faute que le langage laisse passer.
  • Choisir entre while, for et do while selon ce qui est connu d'avance.
  • Compter les tours d'une boucle sans se tromper d'une unité.
  • Employer break et continue, et dire ce que chacun quitte.

Les structures de contrôle du C se retrouvent presque telles quelles dans une bonne moitié des langages qui existent : le if, le while, le for à trois parties, les accolades, le break. Ce n'est pas un hasard de vocabulaire, c'est un héritage direct, et savoir les lire ici revient à savoir les lire en Java, en C++, en C# ou en JavaScript.

Ce chapitre les installe, et s'arrête surtout sur ce que le langage accepte sans broncher : une affectation là où une comparaison était voulue, une boucle dont la condition ne progresse pas, un else qui ne se rattache pas au if qu'on croit. Trois fautes qui ne produisent aucun message, et qui expliquent pourquoi les accolades sont une discipline avant d'être une syntaxe.

Décider

Une condition est une expression entière : zéro vaut faux, tout le reste vaut vrai. Il n'existe pas de type booléen dans le C d'origine, et ce détail explique une bonne part des surprises.

C

La dernière ligne montre le fond de l'affaire : 21 est une condition parfaitement valide, et elle est vraie, parce qu'elle n'est pas nulle.

Un signe égal de trop
if (x = 5) est une instruction valide. Elle range 5 dans x, puis évalue le résultat de cette affectation, qui vaut 5, donc qui est vrai. La branche est toujours prise, et x a été modifiée au passage.
Aucune erreur, aucun avertissement par défaut. C'est la faute qui a fait perdre le plus d'heures dans l'histoire de ce langage, et la parade tient en une habitude : relire à voix haute « si x vaut cinq » et vérifier qu'il y a bien deux signes égal sous les yeux.
À manipuler
Dans le bloc ci-dessus, remplacer temperature > 30 par temperature = 30 et relancer. Le programme annonce « chaud » alors que la température était de 21 degrés, et la variable vaut maintenant 30 pour la suite du programme.
Remettre ensuite == : le programme annonce « tempere », parce que 21 n'est pas égal à 30. Deux caractères séparent les deux comportements, et rien dans la page ne les distingue au premier coup d'œil.

Répéter tant que

while évalue sa condition avant chaque tour. Si elle est fausse au départ, le corps n'est jamais exécuté.

C

Le reste final est négatif, et c'est normal : la condition est vérifiée avant le tour, pas pendant. Un programme qui doit s'arrêter exactement à zéro ne se contente pas de cette boucle, il ajuste le dernier pas.

Une boucle qui ne progresse pas ne s'arrête jamais
Le bloc suivant ne contient aucune faute de syntaxe, et aucun compilateur ne le refuserait. Il oublie seulement de faire avancer la variable de la condition.
L'interpréteur de ces pages s'arrête au bout de trois millions d'opérations et le dit. Un vrai programme, lui, tournerait jusqu'à ce que quelqu'un l'interrompe.
C

Répéter un nombre connu de fois

for rassemble au même endroit les trois choses qui gouvernent une boucle de comptage : l'initialisation, la condition, et le pas.

C

Les deux boucles ci-dessus font chacune un nombre de tours différent de ce que les nombres écrits laissent croire, et c'est le seul point à retenir vraiment.

BouclePremier tourDernier tourNombre de tours
for (i = 1; i <= 10; i++)11010
for (i = 0; i < 10; i++)0910
for (i = 0; i <= 10; i++)01011
for (i = 1; i < 10; i++)199
La forme qui sert partout
for (int i = 0; i < n; i++) fait exactement n tours, avec i allant de 0 à n moins 1.
Ce n'est pas une convention esthétique : c'est la numérotation des cases d'un tableau, qui commence à zéro et finit à n moins 1. Écrire ses boucles ainsi évite d'avoir à convertir au moment où le tableau arrive, au chapitre suivant.

Répéter au moins une fois

do while évalue sa condition après le tour. Le corps est donc exécuté au moins une fois, ce qui convient quand la condition porte sur quelque chose que le tour produit.

C

Écrite avec un while ordinaire, cette boucle donnerait zéro chiffre pour le nombre 0, qui en compte pourtant un. La forme do while répond correctement parce qu'elle divise avant de tester.

Sortir, ou sauter un tour

break quitte la boucle entière. continue abandonne le tour en cours et passe au suivant.

C

Ce programme affiche les nombres premiers jusqu'à trente. Le break quitte la recherche de diviseur dès qu'un diviseur est trouvé, parce que continuer ne changerait plus la réponse ; le continue passe au nombre suivant sans l'afficher. La condition d * d <= n arrête la recherche à la racine carrée, ce qui suffit : un diviseur plus grand que la racine en a forcément un plus petit en face.

À manipuler
Remplacer d * d <= n par d < n : le résultat est le même, et le nombre de tours bien plus grand. Remplacer ensuite par d <= n : la boucle trouve n comme diviseur de lui-même, plus aucun nombre n'est premier, et rien ne s'affiche.
Le troisième essai est le plus instructif : la sortie vide n'est pas une panne, c'est une réponse fausse produite par une condition d'arrêt fausse.

À calculer soi-même

Une boucle se compte avant d'être écrite.

Compter les tours

  • 1.

    Combien de tours fait for (int i = 0; i < 25; i++) ?

  • 2.

    Combien de tours fait for (int i = 1; i <= 25; i++) ?

  • 3.

    Combien de tours fait for (int i = 0; i <= 25; i++) ?

  • 4.

    Combien de tours fait for (int i = 5; i < 50; i += 5) ?

  • 5.

    Après while (reste > 0) { reste = reste - 15; } parti de 100, combien de tours ont eu lieu ?

  • 6.

    Après cette même boucle, que vaut reste ?

  • 7.

    Un nombre de 4 chiffres, divisé par 10 jusqu'à devenir nul : combien de divisions ?

Vérification

Vérification rapideon peut se reprendre

1.Que fait if (x = 5) { ... } ?

2.Combien de tours fait for (int i = 0; i <= 10; i++) ?

3.Quelle est la différence entre while et do while ?

4.Que quitte un break placé dans une boucle imbriquée dans une autre ?

5.Qu'est-ce qu'une condition vraie en C ?

Exercices type

Pourquoi mettre des accolades même autour d'une seule instruction ?

Parce que sans elles, seule la première instruction appartient à la branche ou à la boucle, et que l'indentation ne change rien à cette règle. Le C ignore les espaces, contrairement à Python.

Le cas est célèbre : une branche de sécurité écrite sans accolades, à laquelle une ligne a été ajoutée plus tard, a produit une faille dans une bibliothèque de chiffrement très répandue. La ligne ajoutée s'exécutait toujours, quelle que soit la condition.

Les accolades systématiques coûtent deux caractères et suppriment cette classe entière de fautes.

Comment écrire une boucle qui parcourt les valeurs de 10 à 1, en descendant ?
for (int i = 10; i >= 1; i--) { ... }

Trois choses changent ensemble : la valeur de départ, le sens de la comparaison, et le pas. Changer le pas sans changer la comparaison donne une boucle qui ne fait aucun tour, ou qui n'en finit pas, selon le sens.

Le contrôle qui vaut la peine consiste à se demander, avant d'exécuter : quelle est la première valeur, quelle est la dernière, et combien y en a-t-il.

Un programme doit s'arrêter dès qu'une valeur négative est lue parmi cent mesures. Quelle structure choisir ?

Un for sur les cent mesures, avec un break dès qu'une valeur négative apparaît. Le for porte le nombre maximal de tours, le break porte la condition d'arrêt anticipé, et les deux restent lisibles séparément.

L'autre écriture, un while avec une condition composée, mélange les deux raisons de s'arrêter dans une seule expression, et c'est elle qui produit les conditions d'arrêt fausses.

La méthode

  1. Écrire les accolades, même pour une seule instruction, et même quand personne ne regarde.
  2. Relire chaque = dans une condition, et vérifier qu'il en faut bien deux.
  3. Choisir la boucle d'après ce qui est connu : for quand le nombre de tours est connu d'avance, while quand il dépend de ce qui se passe, do while quand un tour au moins est certain.
  4. Compter les tours avant d'exécuter : première valeur, dernière valeur, nombre de tours.
  5. Vérifier que la variable de la condition progresse dans tous les chemins du corps, continue compris.

Synthèse

  • Une condition est un entier : zéro est faux, tout le reste est vrai.
  • = affecte, == compare. L'affectation dans une condition est valide, et toujours vraie quand la valeur affectée n'est pas nulle.
  • for (int i = 0; i < n; i++) fait exactement n tours, avec i de 0 à n moins 1.
  • while teste avant, do while teste après et exécute donc au moins un tour.
  • break quitte la boucle qui le contient directement ; continue passe au tour suivant.

Et ensuite

Le code écrit jusqu'ici tient dans une seule fonction. Les fonctions et la pile le découpe, et montre où vivent les variables locales : un endroit de la mémoire qui se vide tout seul, et dont il ne faut rien rapporter.

Mettre en pratique