Les sélections

Comme dit au chapitre précédent, les structures de contrôle permettent de modifier le comportement d’un programme suivant la réalisation de différentes conditions. Parmi ces structures de contrôle se trouvent les instructions de sélection (ou sélections en abrégé) qui vont retenir notre attention dans ce chapitre.

Le tableau ci-dessous reprend celles dont dispose le langage C.

Structure de sélection Action
if… exécute une suite d’instructions si une condition est respectée.
if… else… exécute une suite d’instructions si une condition est respectée ou une autre suite dans le cas contraire.
switch… exécute une suite d’instructions différente suivant la valeur testée.

La structure if

Vous savez désormais manipuler des conditions, c’est bien, cependant l’intérêt de la chose reste assez limité pour l’instant. Rendons à présent cela plus intéressant en voyant comment exécuter un bloc d’instruction quand une ou plusieurs conditions sont respectées. C’est le rôle de l’instruction if et de ses consœurs.

L’instruction if

L’instruction if permet d’exécuter un bloc d’instructions si une condition est vérifiée ou de le passer si ce n’est pas le cas.

           |
     +-----v-----+   Faux
     | Condition |---------+
     +-----------+         |
           |               |
      Vrai |               |
           |               |
+----------v----------+    |
| Bloc d'instructions |    |
+---------------------+    |
           |               |
           |<--------------+
           |
           v
   Suite du programme

L’instruction if ressemble à ceci.

if (/* Condition */)
{
    /* Une ou plusieurs instruction(s) */
}

Si la condition n’est pas vérifiée, le bloc d’instructions est passé et le programme recommence immédiatement à la suite du bloc d’instructions délimité par l’instruction if.

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

if (/* Condition */)
    /* Une seule instruction */

Cependant, nous vous conseillons de mettre les accolades systématiquement afin de vous éviter des problèmes si vous décidez de rajouter des instructions par la suite en oubliant d’ajouter des accolades. Bien sûr, ce n’est qu’un conseil, vous êtes libre de ne pas le suivre.

À présent, voyons quelques exemples d’utilisation.

Exemple 1
#include <stdio.h>


int main(void)
{
    int a = 10;
    int b = 20;

    if (a < b)
    {
        printf("%d est inférieur à %d\n", a, b);
    }

    return 0;
}
Résultat
10 est inférieur à 20

L’instruction if évalue l’expression logique a < b, conclut qu’elle est valide et exécute le bloc d’instructions.

Exemple 2
#include <stdio.h>


int main(void)
{
    int a = 10;
    int b = 20;

    if (a > b)
    {
        printf("%d est supérieur à %d\n", a, b);
    }

    return 0;
}

Ce code n’affiche rien. La condition étant fausse, le bloc contenant l’appel à la fonction printf() est ignoré.

La clause else

Avec l’instruction if, nous savons exécuter un bloc d’instructions quand une condition est remplie. Toutefois, si nous souhaitons réaliser une action en cas d’échec de l’évaluation de la condition, nous devons ajouter une autre instruction if à la suite, comme ci-dessous.

if (a > 5)
{
    /* Du code */
}

if (a <= 5)
{
    /* Code alternatif */
}

Le seul problème, c’est qu’il est nécessaire d’ajouter une instruction if et d’évaluer une nouvelle condition, ce qui n’est pas très efficace et assez long à taper. Pour limiter les dégâts, le C offre la possibilité d’ajouter une clause else, qui signifie « sinon ». Celle-ci se place immédiatement après le bloc d’une instruction if et permet d’exécuter un bloc d’instructions alternatif si la condition testée n’est pas vérifiée. Sa syntaxe est la suivante.

if (/* Condition */)
{
    /* Une ou plusieurs instructions */
}
else
{
    /* Une ou plusieurs instructions */
}

Et elle doit être comprise comme ceci.

           |
     +-----v-----+   Faux
     | Condition |--------------------+
     +-----+-----+                    |
           |                          |
      Vrai |                          |
           |                          |
+----------v----------+   +-----------v-----------+
| Bloc d'instructions |   | Bloc d'instructions   |
| de l'instruction if |   | de l'instruction else |
+----------+----------+   +-----------+-----------+
           |                          |
           |<-------------------------+
           |
           v
   Suite du programme
Exemple

Supposons que nous voulions créer un programme très simple auquel nous fournissons une heure et qui indique s’il fait jour ou nuit à cette heure-là. Nous supposerons qu’il fait jour de 8 heures à 20 heures et qu’il fait nuit sinon.

#include <stdio.h>


int main(void)
{
    int heure;

    scanf("%d", &heure);

    if (heure > 8 && heure < 20)
    {
        printf("Il fait jour.\n");
    }
    else
    {
        printf("Il fait nuit.\n");
    }

    return 0;
}
Résultat
10
Il fait jour.
if, else if

Il est parfois nécessaire d’imbriquer plusieurs instructions if else les unes dans les autres, par exemple comme suit.

if (/* Condition(s) */)
{
    /* Instruction(s) */
}
else
{
    if (/* Condition(s) */)
    {
        /* Instruction(s) */
    }
    else
    {
        if (/* Condition(s) */)
        {
            /* Instruction(s) */
        }
        else
        {
            /* Instruction(s) */
        }
    }
}

Ce qui est assez long et lourd à écrire, d’autant plus s’il y a beaucoup d’imbrications… Toutefois et heureusement pour nous, il est possible de simplifier cette écriture.

Tout d’abord, rappelez-vous : si un if ou un else ne comprend qu’une seule instruction, alors les accolades sont facultatives. Nous pouvons donc les retirer pour deux else (une suite if else compte pour une seule instruction).

if (/* Condition(s) */)
{
    /* Instruction(s) */
}
else
    if (/* Condition(s) */)
    {
        /* Instruction(s) */
    }
    else
        if (/* Condition(s) */)
        {
            /* Instruction(s) */
        }
        else
        {
            /* Instruction(s) */
        }

Ensuite, les retours à la ligne n’étant pas obligatoires, nous pouvons « coller » les deux if et les deux else qui se suivent.

if (/* Condition(s) */)
{
    /* Instruction(s) */
}
else if (/* Condition(s) */)
{
    /* Instruction(s) */
}
else if (/* Condition(s) */)
{
    /* Instruction(s) */
}
else
{
    /* Instruction(s) */
}

Notez que comme il s’agit toujours d’une suite d’instructions if else, il n’y aura qu’un seul bloc d’instructions qui sera finalement exécuté. En effet, l’ordinateur va tester la condition de l’instruction if, puis, si elle est fausse, celle de l’instruction if de la clause else et ainsi de suite jusqu’à ce qu’une condition soit vraie (ou jusqu’à une clause else finale si elles sont toutes fausses).

#include <stdio.h>


int main(void)
{
    int heure = 11;

    if (heure > 0 && heure < 7)
    {
        printf("Zzz... \n");
    }
    else if (heure >= 7 && heure < 12)
    {
        printf("C'est le matin !\n");
    }
    else if (heure == 12)
    {
        printf("Il est midi !\n");
    }
    else if (heure > 12 && heure < 18)
    {
        printf("C'est l'après-midi !\n");
    }
    else if (heure >= 18 && heure < 24)
    {
        printf("C'est le soir !\n");
    }
    else if (heure == 24 || heure == 0)
    {
        printf("Il est minuit, dormez brave gens !\n");
    }
    else
    {
        printf("Il est l'heure de réapprendre à lire l'heure !\n");
    }

    return 0;
}
Résultat
11
C'est le matin !
0
Il est minuit, dormez brave gens !
-2
Il est l'heure de réapprendre à lire l'heure !
Exercice

Imaginez que vous avez un score de jeu vidéo sous la main :

  • si le score est strictement inférieur à deux mille, affichez « C’est la catastrophe ! » ;
  • si le score est supérieur ou égal à deux mille et que le score est strictement inférieur à cinq mille, affichez : « Tu peux mieux faire ! » ;
  • si le score est supérieur ou égal à cinq mille et que le score est strictement inférieur à neuf mille, affichez : « Tu es sur la bonne voie ! » ;
  • sinon, affichez : « Tu es le meilleur ! ».

Au boulot ! :)

#include <stdio.h>


int main(void)
{
    int score;

    printf("Quel est le score du joueur ? ");
    scanf("%d", &score);

    if (score < 2000)
    {
        printf("C'est la catastrophe !\n");
    }
    else if (score >= 2000 && score < 5000)
    {
        printf("Tu peux mieux faire !\n");
    }
    else if (score >= 5000 && score < 9000)
    {
        printf("Tu es sur la bonne voie !\n");
    }
    else
    {
        printf("Tu es le meilleur !\n");
    }

    return 0;
}

L'instruction switch

L’instruction switch permet de comparer la valeur d’une expression entière par rapport à une liste de constantes entières. Techniquement, elle permet d’écrire de manière plus concise une suite d’instructions if else qui auraient pour objectif d’accomplir différentes actions suivant la valeur d’une expression.

if (a == 1)
{
    /* Instruction(s) */
}
else if (a == 2)
{
    /* Instruction(s) */
}
/* Etc. */
else
{
    /* Instruction(s) */
}

Avec l’instruction switch, cela donne ceci.

switch (a)
{
case 1:
    /* Instruction(s) */
    break;
case 2:
    /* Instruction(s) */
    break;

/* Etc... */

default: /* Si aucune comparaison n'est juste */
    /* Instruction(s) à exécuter dans ce cas */
    break;
}

Ici, la valeur de la variable a est comparée successivement avec chaque entrée de la liste, indiquées par le mot-clé case. En cas de correspondance, les instructions suivant le mot-clé case sont exécutées jusqu’à rencontrer une instruction break (nous la verrons plus en détail un peu plus tard). Si aucune comparaison n’est bonne, alors ce sont les instructions de l’entrée marquée avec le mot-clé default qui seront exécutées.

Exemple
#include <stdio.h>


int main(void)
{
    int note;

    printf("Quelle note as-tu obtenue (sur cinq) ? ");
    scanf("%d", &note);

    switch(note)
    {
    /* Si note == 0 */
    case 0:
        printf("No comment.\n");
        break;

    /* Si note == 1 */
    case 1:
        printf("Cela te fait 4/20, c'est accablant.\n");
        break;

    /* Si note == 2 */   
    case 2:
        printf("On se rapproche de la moyenne, mais ce n'est pas encore ça.\n");
        break;

    /* Si note == 3 */
    case 3:
        printf("Tu passes.\n");
        break;

    /* Si note == 4*/
    case 4:
        printf("Bon travail, continue ainsi !\n");
        break;

    /* Si note == 5 */
    case 5:
        printf("Excellent !\n");
        break;

    /* Si note est différente de 0, 1, 2, 3, 4 et 5 */
    default:
        printf("Euh... tu possèdes une note improbable...\n");
        break;
    }

    return 0;
}

Notez que comme pour l’instruction else, une entrée marquée avec le mot-clé default n’est pas obligatoire.

Plusieurs entrées pour une même action

Une même suite d’instructions peut être désignée par plusieurs entrées comme le montre l’exemple suivant.

#include <stdio.h>


int main(void)
{
    int note;

    printf("Quelle note as-tu obtenue ? ");
    scanf("%d", &note);

    switch(note)
    {
    /* Si la note est comprise entre zéro et trois inclus */
    case 0:
    case 1:
    case 2:
    case 3:
        printf("No comment.\n");
        break;

    /* Si la note est comprise entre quatre et sept inclus */
    case 4:
    case 5:
    case 6:
    case 7:
        printf("C'est accablant.\n");
        break;

    /* Si la note est comprise entre huit et neuf inclus */  
    case 8:
    case 9:
        printf("On se rapproche de la moyenne, mais ce n'est pas encore ça.\n");
        break;

    /* Si la note est comprise entre dix et douze inclus */
    case 10:
    case 11:
    case 12:
        printf("Tu passes.\n");
        break;

    /* Si la note est comprise entre treize et seize inclus */
    case 13:
    case 14:
    case 15:
    case 16:
        printf("Bon travail, continue ainsi !\n");
        break;

    /* Si la note est comprise entre dix-sept et vingt inclus */
    case 17:
    case 18:
    case 19:
    case 20:
        printf("Excellent !\n");
        break;

    /* Si la note est différente */
    default:
        printf("Euh... tu possèdes une note improbable...\n");
        break;
    }

    return 0;
}
Plusieurs entrées sans instruction break

Sachez également que l’instruction break n’est pas obligatoire. En effet, le but de cette dernière est de sortir du switch et donc de ne pas exécuter les actions d’autres entrées. Toutefois, il arrive que les actions à réaliser se chevauchent entre entrées auquel cas l’instruction break serait plutôt mal venue.

Prenons un exemple : vous souhaitez réaliser un programme qui affiche entre 1 à dix fois la même phrase, ce nombre étant fourni par l’utilisateur. Vous pourriez écrire une suite de if ou différentes entrées d’un switch qui, suivant le nombre entré, appelleraient une fois printf(), puis deux fois, puis trois fois, etc. mais cela serait horriblement lourd.

Dans un tel cas, une meilleure solution consiste à appeler printf() à chaque entrée du switch, mais de ne pas terminer ces dernières par une instruction break.

#include <stdio.h>


int
main(void)
{
    unsigned nb;

    printf("Combien de fois souhaitez-vous répéter l'affichage (entre 1 à 10 fois) ? ");
    scanf("%u", &nb);

    switch (nb)
    {
    case 10:
        printf("Cette phrase est répétée une à dix fois.\n");
    case 9:
        printf("Cette phrase est répétée une à dix fois.\n");
    case 8:
        printf("Cette phrase est répétée une à dix fois.\n");
    case 7:
        printf("Cette phrase est répétée une à dix fois.\n");
    case 6:
        printf("Cette phrase est répétée une à dix fois.\n");
    case 5:
        printf("Cette phrase est répétée une à dix fois.\n");
    case 4:
        printf("Cette phrase est répétée une à dix fois.\n");
    case 3:
        printf("Cette phrase est répétée une à dix fois.\n");
    case 2:
        printf("Cette phrase est répétée une à dix fois.\n");
    case 1:
        printf("Cette phrase est répétée une à dix fois.\n");
    case 0:
        break;
    default:
        printf("Certes, mais encore ?\n");
        break;
    }

    return 0;
}
Résultat
Combien de fois souhaitez-vous répéter l'affichage (entre 1 à 10 fois) ? 2
Cette phrase est répétée une à dix fois.
Cette phrase est répétée une à dix fois.

Combien de fois souhaitez-vous répéter l'affichage (entre 1 à 10 fois) ? 5
Cette phrase est répétée une à dix fois.
Cette phrase est répétée une à dix fois.
Cette phrase est répétée une à dix fois.
Cette phrase est répétée une à dix fois.
Cette phrase est répétée une à dix fois.

Comme vous le voyez, la phrase « Cette phrase est répétée une à dix fois » est affichée une à dix fois suivant le nombre initialement fourni. Cela est possible étant donné l’absence d’instruction break entre les case 10 à 1, ce qui fait que l’exécution du switch continue de l’entrée initiale jusqu’au case 0.

L'opérateur conditionnel

L’opérateur conditionnel ou opérateur ternaire est un opérateur particulier dont le résultat dépend de la réalisation d’une condition. Son deuxième nom lui vient du fait qu’il est le seul opérateur du langage C à requérir trois opérandes : une condition et deux expressions.

(condition) ? expression si vrai : expression si faux

Les parenthèses entourant la condition ne sont pas obligatoires, mais préférables.

Grosso modo, cet opérateur permet d’écrire de manière condensée une structure if {} else {}. Voyez par vous-mêmes.

#include <stdio.h>

int main(void)
{
    int heure;

    scanf("%d", &heure);

    (heure > 8 && heure < 20) ? printf("Il fait jour.\n") : printf("Il fait nuit.\n");
    return 0;
}

Il est également possible de l’écrire sur plusieurs lignes, même si cette pratique est moins courante.

(heure > 8 && heure < 20)
    ? printf("Il fait jour.\n")
    : printf("Il fait nuit.\n");

Cet opérateur peut sembler inutile de prime abord, mais il s’avère être un allié de choix pour simplifier votre code quand celui-ci requiert la vérification de conditions simples.

Exercice

Pour bien comprendre cette nouvelle notion, nous allons faire un petit exercice. Imaginez que nous voulions faire un mini jeu vidéo dans lequel nous affichons le nombre de coups du joueur. Seulement voilà, vous êtes maniaques du français et vous ne supportez pas qu’il y ait un « s » en trop ou en moins. Essayez de réaliser un programme qui demande à l’utilisateur d’entrer un nombre de coups puis qui affiche celui-ci correctement accordé.

#include <stdio.h>

int main(void)
{
    int nb_coups;

    printf("Donnez le nombre de coups : ");
    scanf("%d", &nb_coups);
    printf("Vous gagnez en %d coup%c\n", nb_coups, (nb_coups > 1) ? 's' : ' ');
    return 0;
}

Ce programme utilise l’opérateur conditionnel pour condenser l’expression et aller plus vite dans l’écriture du code. Sans lui nous aurions dû écrire quelque chose comme ceci.

#include <stdio.h>

int main(void)
{
    int nb_coups;

    printf("Donnez le nombre de coups : ");
    scanf("%d", &nb_coups);

    if (nb_coups > 1)
        printf("Vous gagnez en %d coups\n", nb_coups);
    else
        printf("Vous gagnez en %d coup\n", nb_coups);

    return 0;
}

Ce chapitre a été important, il vous a permis d’utiliser les conditions, l’instruction if, l’instruction switch et l’opérateur conditionnel. Aussi, si vous n’avez pas très bien compris ou que vous n’avez pas tout retenu, nous vous conseillons de relire ce chapitre.

Le chapitre suivant sera l’occasion de mettre en œuvre ce que vous avez appris puisqu’il s’agira de votre premier TP.

En résumé
  1. L’instruction if permet de conditionner l’exécution d’une suite d’instructions ;
  2. Une clause else peut compléter une instruction if afin d’exécuter une autre suite d’instructions dans le cas où la condition est fausse ;
  3. L’instruction switch permet de comparer la valeur d’une expression à une liste de constantes entières ;
  4. Dans le cas où une comparaison est vraie, les instructions exécutées sont celles suivant un case et précédant une instruction break ;
  5. Plusieurs case peuvent précéder la même suite d’instructions ;
  6. L’opérateur conditionnel peut être utilisé pour simplifier l’écriture de certaines conditions.