Les boucles

Dans ce chapitre, nous allons aborder les boucles. Une boucle est un moyen de répéter des instructions suivant le résultat d’une condition. Ces structures, dites itératives, que nous allons voir dans ce chapitre sont les suivantes.

Structure itérative Action
while… répète une suite d’instructions tant qu’une condition est respectée.
do… while… répète une suite d’instructions tant qu’une condition est respectée. Le groupe d’instructions est exécuté au moins une fois.
for… répète un nombre fixé de fois une suite d’instructions.

La boucle while

La première des boucles que nous allons étudier est la boucle while (qui signifie « tant que »). Celle-ci permet de répéter un bloc d’instructions tant qu’une condition est remplie.

+------------>|
|             |
|       +-----v-----+   Faux
|       | Condition |-------------------+
|       +-----------+                   |
|             |                         |
|        Vrai |                         |
|             |                         |
|   +---------v-----------+   +---------v----------+
|   | Bloc d'instructions |   | Suite du programme |
|   | à répéter           |   +--------------------+
|   +---------------------+
|             |
+-------------+
Syntaxe

La syntaxe de notre boucle while est assez simple.

while (/* Condition */)
{
    /* Bloc d'instructions à répéter */ 
}

Si vous n’avez qu’une seule instruction à réaliser, vous avez la possibilité de ne pas mettre d’accolades.

while (/* Condition */)
    /* Une seule instruction */
Exemple
#include <stdio.h>


int main(void)
{
    int i = 0;

    while (i < 5)
    {
        printf("La variable i vaut %d\n", i);
        i++;
    }

    return 0;
}
Résultat
La variable i vaut 0
La variable i vaut 1
La variable i vaut 2
La variable i vaut 3
La variable i vaut 4

Le fonctionnement est simple à comprendre :

  • Au départ, notre variable i vaut zéro. Étant donné que zéro est bien inférieur à cinq, la condition est vraie, le corps de la boucle est donc exécuté.
  • La valeur de i est affichée.
  • i est augmentée d’une unité et vaut désormais un.
  • La condition de la boucle est de nouveau vérifiée.

Ces étapes vont ainsi se répéter pour les valeurs un, deux, trois et quatre. Quand la variable i vaudra cinq, la condition sera fausse, et l’instruction while sera alors passée.

Dans cet exemple, nous utilisons une variable nommée i. Ce nom provient des mathématiques où il est utilisé pour la notation des sommes de suite de termes, par exemple comme celle-ci i=24i2=22+32+42=29\sum_{i=2}^4 i^2 = 2^2 + 3^2 + 4^2 = 29. Ce nom est devenu une convention de nommage en C, vous le rencontrerez très souvent. Dans le cas où d’autres variables sont nécessaires, il est d’usage de poursuivre l’ordre alphabétique, soit j, k, l, etc.

Exercice

Essayez de réaliser un programme qui détermine si un nombre entré par l’utilisateur est premier. Pour rappel, un nombre est dit premier s’il n’est divisible que par un et par lui-même. Notez que si un nombre xx est divisible par yy alors le résultat de l’opération x % y est nul.

Indice

Pour savoir si un nombre est premier, il va vous falloir vérifier si celui-ci est uniquement divisible par un et lui-même. Dit autrement, vous allez devoir contrôler qu’aucun nombre compris entre 1 et le nombre entré (tout deux exclus) n’est un diviseur de ce dernier. Pour parcourir ces différentes possibilités, une boucle va vous être nécessaire.

Correction
#include <stdio.h>


int main(void)
{
   int nombre;

   printf("Entrez un nombre : ");
   scanf("%d", &nombre);

   int i = 2;

   while ((i < nombre) && (nombre % i != 0))
   {
       ++i;
   }

   if (i == nombre)
   {
       printf("%d est un nombre premier\n", nombre);
   }
   else
   {
       printf("%d n'est pas un nombre premier\n", nombre);
   }

   return 0;
}

La boucle do-while

La boucle do while fonctionne comme la boucle while, à un petit détail près : elle s’exécutera toujours au moins une fois, alors qu’une boucle while peut ne pas s’exécuter si la condition est fausse dès le départ.

+------------>|
|             |
|   +---------v-----------+
|   | Bloc d'instructions |
|   | à répéter           |
|   +---------------------+
|             |
|             |
|             |
|       +-----v-----+    Faux
|       | Condition +-------------+
|       +-----------+             |
|             |                   |
+-------------+                   |
       Vrai                       |
                        +---------v----------+
                        | Suite du programme |
                        +--------------------+
Syntaxe

À la différence de la boucle while, la condition est placée à la fin du bloc d’instruction à répéter, ce qui explique pourquoi celui-ci est toujours exécuté au moins une fois. Remarquez également la présence d’un point-virgule à la fin de l’instruction qui est obligatoire.

do
{
    /* Bloc d'instructions à répéter */
} while (/* Condition */);

Si vous n’avez qu’une seule instruction à exécuter, les accolades sont facultatives.

do
    /* Une seule instruction */
while (/* Condition */);
Exemple 1

Voici le même code que celui présenté avec l’instruction while.

#include <stdio.h>


int main(void)
{
    int i = 0;

    do
    {
        printf("La variable i vaut %d\n", i);
        ++i;
    } while (i < 5);

    return 0;
}
Résultat
La variable i vaut 0
La variable i vaut 1
La variable i vaut 2
La variable i vaut 3
La variable i vaut 4
Exemple 2

Comme nous vous l’avons dit plus haut, une boucle do while s’exécute au moins une fois.

#include <stdio.h>


int main(void)
{    
    do
        printf("Boucle do-while\n");
    while (0);

    return 0;
}
Boucle do-while

Comme vous le voyez, bien que la condition est fausse (pour rappel, une valeur nulle correspond à une valeur fausse), le corps de la boucle est exécuté une fois puisque la condition n’est évaluée qu’après le parcours du bloc d’instructions.

La boucle for

Syntaxe
for (/* Expression/Déclaration */; /* Condition */; /* Expression */)
{
    /* Instructions à répéter */
}

Une boucle for se décompose en trois parties (ou trois clauses) :

  • une expression et/ou une déclaration qui sera le plus souvent l’initialisation d’une variable ;
  • une condition ;
  • une seconde expression, qui consistera le plus souvent en l’incrémentation d’une variable.

Techniquement, une boucle for revient en fait à écrire ceci.

/* Expression/Déclaration */

while (/* Condition */)
{
    /* Bloc d'instructions à répéter */
    /* Expression */
}

Comme pour les deux autres boucles, si le corps ne comporte qu’une seule instruction, les accolades ne sont pas nécessaires.

for (/* Expression/Déclaration */; /* Condition */; /* Expression */)
    /* Une seule instruction */
Exemple

Le fonctionnement de cette boucle est plus simple à appréhender à l’aide d’un exemple.

#include <stdio.h>


int main(void)
{
    for (int i = 0; i < 5; ++i)
        printf("la variable i vaut %d\n", i);

    return 0;
}
Résultat
variable vaut 0
variable vaut 1
variable vaut 2
variable vaut 3
variable vaut 4

Ce qui, comme dit précédemment, revient exactement à écrire cela.

#include <stdio.h>


int main(void)
{
    int i = 0;

    while (i < 5)
    {
        printf("la variable i vaut %d\n", i);
        ++i;
    }

    return 0;
}

Notez bien que la déclaration int i = 0 est située en dehors du corps de la boucle. Elle n’est donc pas exécutée à chaque tour.

Si vous déclarez une variable au sein de l’instruction for, celle-ci ne sera utilisable qu’au sein de cette boucle. Le code suivant est donc incorrect.

#include <stdio.h>


int main(void)
{
    for (int i = 0; i < 5; ++i)
        printf("la variable i vaut %d\n", i);

    printf("i = %d\n", i); /* Faux, i n'est pas utilisable hors de la boucle for */
    return 0;
}

Dans un tel cas, il est nécessaire de déclarer la variable avant et en dehors de la boucle for.

#include <stdio.h>


int main(void)
{
    int i;

    for (i = 0; i < 5; ++i)
        printf("la variable i vaut %d\n", i);

    printf("i = %d\n", i); /* Ok */
    return 0;
}
Exercice

Essayez de réaliser un programme qui calcule la somme de tous les nombres compris entre un et nn (nn étant déterminé par vos soins). Autrement dit, pour un nombre nn donné, vous allez devoir calculer 1+2+3+...+(n2)+(n1)+n1 + 2 + 3 + ... + (n-2) + (n-1) + n.

#include <stdio.h>


int main (void)
{
    const unsigned int n = 250;
    unsigned somme = 0;

    for (unsigned i = 1; i <= n; ++i)
        somme += i;

    printf ("Somme de 1 à %u : %u\n", n, somme);
    return 0;
}

Notez qu’il est possible de réaliser cet exercice sans boucle en calculant : N×(N+1)2\frac {N \times (N+1)} {2}.

Plusieurs compteurs

Notez que le nombre de compteurs, de déclarations ou de conditions n’est pas limité, comme le démontre le code suivant.

for (int i = 0, j = 2; i < 10 && j < 12; i++, j += 2)

Ici, nous définissons deux compteurs i et j initialisés respectivement à zéro et deux. Le contenu de la boucle est exécuté tant que i est inférieur à dix et que j est inférieur à douze, i étant augmentée de une unité et j de deux unités à chaque tour de boucle. Le code est encore assez lisible, cependant la modération est de mise, un trop grand nombre de paramètres rendant la boucle for illisible.

Imbrications

Il est parfaitement possible d’imbriquer une ou plusieurs boucles en plaçant une boucle dans le corps d’une autre boucle.

for (int i = 0; i < 1000; ++i)
{
    for (int j = i; j < 1000; ++j)
    {
          /*  Code  */
    }
}

Cela peut servir par exemple pour déterminer la liste des nombres dont le produit vaut mille.

#include <stdio.h>


int main(void)
{
    for (int i = 0; i <= 1000; ++i)
        for (int j = i; j <= 1000; ++j)
            if (i * j == 1000) 
                printf ("%d * %d = 1000 \n", i, j);

    return 0;
}
Résultat
1 * 1000 = 1000 
2 * 500 = 1000 
4 * 250 = 1000 
5 * 200 = 1000 
8 * 125 = 1000 
10 * 100 = 1000 
20 * 50 = 1000 
25 * 40 = 1000 

Vous n’êtes bien entendu pas tenu d’imbriquer des types de boucles identiques. Vous pouvez parfaitement plaçer, par exemple, une boucle while dans une boucle for.

Boucles infinies

Lorsque vous utilisez une boucle, il y a une chose que vous devez impérativement vérifier : elle doit pouvoir se terminer. Cela paraît évident de prime abord, pourtant il s’agit d’une erreur de programmation assez fréquente qui donne lieu à des boucles infinies. Soyez donc vigilants !

L’exemple le plus fréquent est l’oubli d’incrémentation de l’itérateur.

#include <stdio.h>

int main(void)
{
    int i = 0;

    while (i < 5)
    {
        printf("La variable i vaut %d\n", i);
        /* Oubli d'incrémentation. */
    }

    return 0;
}
Résultat
La variable i vaut 0
La variable i vaut 0
La variable i vaut 0
...

Ce programme continuera son exécution jusqu’à ce qu’il soit arrêté.

Pour stopper l’exécution d’un programme, vous pouvez utiliser la combinaison Ctrl + c ou simplement fermer votre terminal.

Exercices

Calcul du PGCD de deux nombres

Le PGCD de deux nombres est le plus grand nombre qui peut diviser ces derniers. Par exemple, le PGCD de quinze et douze est trois et celui de vingt-quatre et dix-huit est six.

Pour le calculer, nous devons disposer de deux nombres aa et bb avec aa supérieur à bb. Ensuite, nous effectuons la division entière de aa par bb.

  • si le reste est nul, alors nous avons terminé ;
  • si le reste est non nul, nous revenons au début en remplaçant aa par bb et bb par le reste.

Avec cette explication, vous avez tout ce qu’il vous faut : à vos claviers !

Correction
#include <stdio.h>


int main (void)
{
    unsigned int a = 46;
    unsigned int b = 42;
    unsigned int reste = a % b;

    while (reste != 0)
    {
        a = b;
        b = reste;
        reste = a % b;
    }

    printf("%d", b);
    return 0;
}
Une overdose de lapins

Au treizième siècle, un mathématicien italien du nom de Leonardo Fibonacci posa un petit problème dans un de ses livres, qui mettait en scène des lapins. Ce petit problème mit en avant une suite de nombres particulière, nommée la suite de Fibonnaci, du nom de son inventeur. Il fit les hypothèses suivantes :

  • le premier mois, nous plaçons un couple de deux lapins dans un enclos ;
  • un couple de lapin ne peut procréer qu’à partir du troisième mois de sa venue dans l’enclos (autrement dit, il ne se passe rien pendant les deux premiers mois) ;
  • chaque couple capable de procréer donne naissance à un nouveau couple tous les mois ;
  • enfin, pour éviter tout problème avec la SPA, les lapins ne meurent jamais.

Le problème est le suivant : combien y a-t-il de couples de lapins dans l’enclos au n-ième mois ? Le but de cet exercice est de réaliser un petit programme qui fasse ce calcul automatiquement.

Indice

Allez, un petit coup de pouce : suivant l’énoncé, un couple ne donne naissance à un autre couple qu’au début du troisième mois de son apparition. Combien de couple y a-t-il le premier mois ? Un seul. Combien y en a-t-il le deuxième mois ? Toujours un seul. Combien y en a-t-il le troisième mois (le premier couple étant là depuis deux mois) ? Deux. Avec ceci, vous devriez venir à bout du problème.

Correction
#include <stdio.h>


int main(void)
{
    int a = 0;
    int b = 1;
    int nb_lapins = 1;
    int const mois = 10;

    for (int i = 1; i < mois; ++i)
    {
        nb_lapins = a + b;
        a = b;
        b = nb_lapins;
    }

    printf("Au mois %d, il y a %d couples de lapins\n", mois, nb_lapins);
    return 0;	
}
Des pieds et des mains pour convertir mille miles

Si vous avez déjà voyagé en Grande-Bretagne ou aux États-unis, vous savez que les unités de mesure utilisées dans ces pays sont différentes des nôtres. Au lieu de notre cher système métrique, dont les stars sont les centimètres, mètres et kilomètres, nos amis outre-manche et outre-atlantique utilisent le système impérial, avec ses pouces, pieds et miles, voire lieues et furlongs ! Et pour empirer les choses, la conversion n’est pas toujours simple à effectuer de tête… Aussi, la lecture d’un ouvrage tel que Le Seigneur des Anneaux, dans lequel toutes les distances sont exprimées en unités impériales, peut se révéler pénible.

Grâce au langage C, nous allons aujourd’hui résoudre tous ces problèmes ! Votre mission, si vous l’acceptez, sera d’écrire un programme affichant un tableau de conversion entre miles et kilomètres. Le programme ne demande rien à l’utilisateur, mais doit afficher quelque chose comme ceci.

Miles    Km
5        8
10       16
15       24
20       32
25       40
30       48

Autrement dit, le programme compte les miles de cinq en cinq jusqu’à trente et affiche à chaque fois la valeur correspondante en kilomètres. Un mile vaut exactement 1.609344 km, cependant nous allons utiliser une valeur approchée : huit-cinquièmes de kilomètre (soit 1.6km). Autrement dit, 11 miles =85= \frac{8}{5} km ou (11 km =58= \frac{5}{8} miles).

Correction
#include <stdio.h>


int main(void)
{
    unsigned miles = 0;

    printf("Miles\tKm\n");

    do
    {
        ++miles;
        printf("%u\t%u\n", miles * 5, miles * 8);
    } while (miles * 5 < 30);
    
    return 0;
}
Puissances de trois

Passons à un exercice un peu plus difficile, du domaine des mathématiques. Essayez de le faire même si vous n’aimez pas les mathématiques.

Vous devez vérifier si un nombre est une puissance de trois, et afficher le résultat. De plus, si c’est le cas, vous devez afficher l’exposant qui va avec.

Indice

Pour savoir si un nombre est une puissance de trois, vous pouvez utiliser le modulo. Attention cependant : si le reste vaut 0, le nombre n’est pas forcément une puissance de trois (par exemple, le reste de la division de 15 par 3 est nul, mais 15 n’est pas une puissance de trois).

Correction
#include <stdio.h>


/* Un petite explication s'impose, notamment au niveau du for. La première
partie qui correspond à l'initialisation de i ne devrait pas vous poser
trop de soucis. Ensuite, le i /= 3 sert à diviser i par trois à chaque
itération. Au tour de la condition, le principe est simple : tant que le
reste de la division de i par 3 est égal à zéro et que i est positif, on
incrémente l'exposant. Enfin, pour déterminer si le nombre est une puissance
de trois, il suffit de vérifier si i est égal à 1 (essayez avec de petits
nombres dans votre tête ou sur papier si vous n'êtes pas convaincu). */ 

int main(void)
{
    int number;

    printf("Veuillez entrez un nombre : ");
    scanf("%d", &number);

    int exposant = 0;
    int i;

    for (i = number; (i % 3 == 0) && (i > 0); i /= 3)
        ++exposant;

    /* Chaque division successive divise i par 3, donc si nous obtenons finalement
    i == 1, c'est que le nombre est bien une puissance de 3 */

    if (i == 1)
        printf ("%d est égal à 3 ^ %d\n", number, exposant);
    else
        printf("%d n'est pas une puissance de 3\n", number);

    return 0;
}
La disparition : le retour

Connaissez-vous le roman La Disparition ? Il s’agit d’un roman français de Georges Perec, publié en 1969. Sa particularité est qu’il ne contient pas une seule fois la lettre « e ». On appelle ce genre de textes privés d’une lettre des lipogrammes. Celui-ci est une prouesse littéraire, car la lettre « e » est la plus fréquente de la langue française : elle représente une lettre sur six en moyenne ! Le roman faisant environ trois cents pages, il a sûrement fallu déployer des trésors d’inventivité pour éviter tous les mots contenant un « e ».

Si vous essayez de composer un tel texte, vous allez vite vous rendre compte que vous glissez souvent des « e » dans vos phrases sans même vous en apercevoir. Nous avons besoin d’un vérificateur qui nous sermonnera chaque fois que nous écrirons un « e ». C’est là que le langage C entre en scène !

Écrivez un programme qui demande à l’utilisateur de taper une phrase, puis qui affiche le nombre de « e » qu’il y a dans celle-ci. Une phrase se termine toujours par un point « . », un point d’exclamation « ! » ou un point d’interrogation « ? ». Pour effectuer cet exercice, il sera indispensable de lire la phrase caractère par caractère.

Entrez une phrase : Bonjour, comment allez-vous ?
Au moins une lettre 'e' a été repérée (précisément : 2) !
Indice

La première chose à faire est d’afficher un message de bienvenue, afin que l’utilisateur sache quel est votre programme. Ensuite, Il vous faudra lire les caractères tapés (rappelez-vous les différents formats de la fonction scanf()), un par un, jusqu’à ce qu’un point (normal, d’exclamation ou d’interrogation) soit rencontré. Dans l’intervalle, il faudra compter chaque « e » qui apparaîtra. Enfin, il faudra afficher le nombre de « e » qui ont été comptés (potentiellement aucun).

Correction
#include <stdio.h>

int main(void)
{
    printf("Entrez une phrase se terminant par '.', '!' ou '?' : ");

    unsigned compteur = 0;
    char c;

    do
    {
        scanf("%c", &c);

        if (c == 'e' || c == 'E')
            compteur++;
    } while (c != '.' && c != '!' && c != '?');
    
    if (compteur == 0)
        printf("Aucune lettre 'e' repérée. Félicitations !\n");
    else
        printf("Au moins une lettre 'e' a été repérée (précisémment : %d) !\n", compteur);
    
    return 0;
}

Les boucles sont assez faciles à comprendre, la seule chose dont il faut se souvenir étant de faire attention de bien avoir une condition de sortie pour ne pas tomber dans une boucle infinie. Le prochain chapitre abordera la notion de saut.

En résumé
  1. Une boucle permet de répéter l’exécution d’une suite d’instructions tant qu’une condition est vraie ;
  2. La boucle while évalue une condition avant d’exécuter une suite d’instructions ;
  3. La boucle do while évalue une condition après avoir exécuté une suite d’instructions ;
  4. La boucle for est une forme condensée de la boucle while ;
  5. Une variable définie dans la première clause d’une boucle for ne peut être utilisée qu’au sein de cette boucle ;
  6. Une boucle dont la condition est toujours vraie est appelée une boucle infinie.