Tous droits réservés

Le conditionnel conjugué en C++

« Le conditionnel est un mode employé pour exprimer un état soumis à une condition. » Telle est la définition qu’en donne Wikipédia. Cela vous rappelle-t-il vos cours de français ? Mais quel peut bien être le rapport avec la programmation ? C’est que C++ permet aussi d’exprimer des conditions et donc de modifier le comportement de notre programme.

Ce chapitre va donc introduire les conditions à notre palette d’outils et permettra de résoudre la problématique soulevée en conclusion du chapitre précédent.

Les booléens

Les conditions en C++ sont régies par un principe simple, qui est qu'une condition est soit vraie, soit fausse. Finalement, c’est un peu comme dans la réalité. Si je vous pose la question « Êtes-vous majeurs ? », la réponse est soit oui, soit non. De même, « Faîtes-vous 1m80 ou plus ? » entraînera soit une réponse positive, soit négative.

C++ nous offre un type conçu exprès pour ça, bool. Ce type peut prendre deux valeurs : soit true, signifiant vrai, soit false qui veut dire faux. Voyez par vous-mêmes l’exemple on ne peut plus bête ci-dessous.

int main()
{
    bool const vrai { true };
    bool const faux { false };
    
    return 0;
}

D’accord, mais ça sert à quoi ça ? Je vois l’intérêt de manipuler des entiers ou du texte, mais stocker juste vrai ou faux, ça sert quand ?

L’intérêt semble en effet très faible si l’on se contente du code précédent. Sachez que les booléens sont partout, utilisables grâce à ce que l’on appelle les opérateurs de comparaisons. En voici la liste juste ci-dessous.

Opérateur Signification Exemple
== Égalité, compare si deux variables sont égales entre elles. a == b
!= Inégalité, compare si deux variables sont de valeurs différentes. a != b
< Strictement inférieur, compare si la variable de gauche est strictement inférieure à celle de droite. a < b
<= Inférieur ou égale, compare si la variable de gauche est inférieure ou égale à celle de droite. a <= b
> Strictement supérieur, compare si la variable de gauche est strictement supérieure à celle de droite. a > b
>= Supérieur ou égale, compare si la variable de gauche est supérieure ou égale à celle de droite. a >= b
Opérateur d’égalité

Non, == n’est pas une erreur. Il faut bien deux signes =, pour différencier avec l’opération consistant à modifier la valeur d’une variable.

Le rapport avec les booléens ? Chacune des expressions suivantes renvoie true ou false. Faisons donc un petit test.

#include <iostream>

int main()
{
    int const a { 10 };
    int const b { 20 };

    // Cette directive nous permet d'afficher true ou false. Par défaut, std::cout affiche 1 ou 0.
    std::cout << std::boolalpha;

    std::cout << "a == b donne " << (a == b) << std::endl;
    std::cout << "a != b donne " << (a != b) << std::endl;
    std::cout << "a < b donne " << (a < b) << std::endl;
    std::cout << "a <= b donne " << (a <= b) << std::endl;

    // On peut tout à fait stocker le résultat dans une variable booléenne.
    bool const plus_grand { a > b };
    std::cout << "a > b donne " << plus_grand << std::endl;

    bool const plus_grand_ou_egal { a >= b };
    std::cout << "a >= b donne " << plus_grand_ou_egal << std::endl;

    return 0;
}

Lancez donc ce code, analysez les résultats. Sont-ils logiques ? Maintenant, changez la valeur des variables et observez de nouveau. Est-ce que c’est toujours logique ? Voyez-vous maintenant un peu plus l’utilité des booléens ? :)

if — Si, et seulement si…

Maintenant, examinons la façon de poser une question en C++, si l’on peut dire. Nous allons en effet découvrir la première structure de contrôle : if. Ce mot-clef est un mot anglais signifiant « si », et exécute des instructions si, et seulement si la condition donnée est vraie. Voyez donc ce schéma.

Image originale tirée du tutoriel sur le langage C.
Image originale tirée du tutoriel sur le langage C.

En C++, voici à quoi ressemble cette instruction. Toutes les instructions entre accolades seront exécutées si condition est vraie.

if (/* condition */)
{
    // Code à exécuter si la condition est vraie.
}

Prenons un exemple. Affichons la note à un examen, rentrée par l’utilisateur. Si celle-ci est supérieure ou égale à 16, nous afficherons un message de félicitations.

#include <iostream>

int main()
{
    std::cout << "Indique-moi ta note : ";

    int note { 0 };
    std::cin >> note;

    std::cout << "Tu as obtenu " << note << std::endl;
    
    if (note >= 16)
    {
        std::cout << "Félicitations, c'est une très bonne note !" << std::endl;
    }

    return 0;
}

Faites-le tourner. Vous verrez, le message de félicitations ne s’affiche que si vous rentrez un nombre supérieur ou égal à 16. Essayez avec 15, vous ne verrez plus le message.

À portée

Nous pouvons également déclarer des variables au sein du bloc d’accolades. Cependant, celles-ci ne sont utilisables que jusqu’à l’accolade fermante correspondante. Ainsi, le code ci-dessous n’est pas correct.

#include <iostream>

int main()
{
    if (true)
    {
        int const age { 42 };
        // D'autres instructions.

        // Aucun problème.
        if (age == 42)
        {
            // Ici non plus.
        }
    }

    // NON ! Le compilateur ne connait pas la variable age.
    std::cout << age << std::endl;

    return 0;
}

Ce sont les règles de C++ qui veulent ça. Quand on écrit un nouveau bloc à base d’accolades, on dit qu’on crée une nouvelle portée. Et une variable n’est utilisable que dans la portée, ou le bloc d’accolade, où elle a été déclarée.

C’est une bonne pratique de déclarer ses variables dans la plus petite portée possible. Il y a plusieurs raisons à ça.

  • Dans le cas d’un code long et complexe, déclarer une variable au plus près possible de son utilisation permet de ne pas avoir à parcourir de longues lignes pour en obtenir des détails (quel est son type, sa valeur de base, etc). Cela aide à la lecture et la compréhension du code.
  • Lorsqu’on atteint la fin du bloc correspondant, le programme libère dans la mémoire les emplacements qu’il avait réservés pour les variables qui s’y trouvaient. Dans le cas d’un int comme ici, ça ne change rien. Mais dans le cas de variables plus complexes que nous verrons plus tard, cela peut avoir une grande incidence sur les performances du programme.

Ainsi, je vous encourage fortement à déclarer vos variables à l’intérieur de vos blocs de condition si elles ne sont pas destinées à être utilisées ailleurs.

else — Sinon…

Maintenant que nous avons vu comment changer le code si une condition est vérifiée, voyons comment faire l’opposé, c’est-à-dire comment agir si celle-ci est fausse. Imaginons un programme demandant si l’utilisateur est majeur ou mineur. On peut imaginer quelque chose comme ce qui suit.

#include <iostream>

int main()
{
    std::cout << "Donne-moi ton âge: ";
    int age { 0 };
    std::cin >> age;

    if (age >= 18)
    {
        std::cout << "Tu es majeur." << std::endl;
    }

    if (age < 18)
    {
        std::cout << "Tu es mineur." << std::endl;
    }

    return 0;
}

Ce programme est fonctionnel mais un peu lourd, puisqu’on répète deux fois une condition quasiment identique. Heureusement, C++ nous offre un autre mot-clef, else, qui exécute des instructions si la condition du if est fausse. Voyez-vous mêmes le code ci-dessous.

#include <iostream>

int main()
{
    std::cout << "Donne-moi ton âge: ";
    int age { 0 };
    std::cin >> age;

    if (age >= 18)
    {
        std::cout << "Tu es majeur." << std::endl;
    }
    else
    {
        std::cout << "Tu es mineur." << std::endl;
    }

    return 0;
}

Le code se lit ainsi en français : « Si l’âge rentré par l’utilisateur est supérieur ou égal à 18 ans, alors afficher “Tu es majeur”, sinon afficher “Tu es mineur”. » Le mot-clef else est en effet un mot d’anglais qui existe et qui signifie, comme vous l’avez compris, « sinon ».

Image originale tirée du [tutoriel sur le langage C](https://zestedesavoir.com/contenus/755/le-langage-c-1/1042_les-bases-du-langage-c/4294_les-selections/#1-12882_la-structure-if).
Image originale tirée du [tutoriel sur le langage C](https://zestedesavoir.com/contenus/755/le-langage-c-1/1042_les-bases-du-langage-c/4294_les-selections/#1-12882_la-structure-if).

Notez bien que, contrairement au if, else n’a pas de parenthèse pour préciser une condition. En effet, rappelons que else signifie « tout le reste ». Y’a t-il donc un moyen de tester plusieurs conditions différentes avant de faire « tout le reste » ? Sommes-nous condamnés à utiliser une suite de if et de else ?

else if — La combinaison des deux précédents

Comme vous l’avez vu en lisant le titre de cette section, C++ est un langage bien conçu qui répond à la problématique de la section précédente. La combinaison else if s’utilise en effet entre un if et un else pour dire « ou si cette condition est vraie. » Voyons déjà à quoi ressemble la syntaxe pour vous donner une idée.

if (/* condition 1 */)
{
    // Si
    // Code à exécuter si la condition 1 est vraie.
}
else if (/* condition 2 */)
{
    // Ou si.
    // Code à exécuter si la condition 1 est fausse mais que la condition 2 est vraie.
}
else if (/* condition 3 */)
{
    // Ou si.
    // Code à exécuter si ni la condition 1 ni la condition 2 ne sont vraies mais que la condition 3 est vraie.
}
else
{
    // Sinon.
    // Code à exécuter si aucune condition n'est vraie.
}

Vous voyez le principe ? Bien, passons donc à un exercice concret. Imaginons un programme qui affiche des messages différents en fonction de l’âge de l’utilisateur. Mettez le texte et choisissez les âges que vous souhaitez. Une solution parmi d’autre se trouve ci-dessous.

#include <iostream>

int main()
{
    std::cout << "Quel âge as-tu ? ";

    unsigned int age { 0 };
    std::cin >> age;

    if (age < 5u)
    {
        std::cout << "Tu n'es pas un peu jeune pour naviguer sur internet ?" << std::endl;
    }
    else if (age < 18u)
    {
        std::cout << "Tu es mineur !" << std::endl;
    }
    else if (age < 30u)
    {
        std::cout << "Vous êtes majeur !" << std::endl;
    }
    else if (age < 50u)
    {
        std::cout << "Vous n'êtes plus très jeunes, mais vous n'êtes pas encore vieux." << std::endl;
    }
    else if (age < 70u)
    {
        std::cout << "Vous commencez à prendre de l'âge." << std::endl;
    }
    else if (age < 120u)
    {
        std::cout << "Vous avez du en voir, des choses, durant votre vie !" << std::endl;
    }
    else
    {
        std::cout << "Quelle longévité !" << std::endl;
    }

    return 0;
}

Conditions imbriquées

Il est tout à fait possible de tester des conditions au sein d’autres conditions. Si l’on prend l’exemple d’un menu de restaurant, on peut imaginer demander au client s’il veut de la viande ou un plat végétarien. Dans le premier cas, il faut lui demander la cuisson souhaitée ; dans le deuxième, on lui laisse le choix du légume. Cela est tout à fait possible en C++.

#include <iostream>

int main()
{
    std::cout << "Bienvenue au restaurant \"Chez Clem\"." << std::endl;

    bool const plat_vegetarien { true };

    if (plat_vegetarien)
    {
        bool const salade { true };
        if (salade)
        {
            std::cout << "Salade grecque ? De pâtes ?" << std::endl;
        }
        else
        {
            std::cout << "Nous avons également de la purée, une macédoine de légumes, une omelette..." << std::endl;
        }
    }
    else
    {
        bool const viande { true };
        if (viande)
        {
            std::cout << "Bleue, saignante, à point, bien cuite ?" << std::endl;
        }
        else
        {
            std::cout << "Sinon niveau poisson il y a pavé de saumon, filet de cabillaud..." << std::endl;
        }
    }

    return 0;
}
Instruction condensée

L’instruction else if, vue plus haut, correspond en fait à un if dans un else.

if (/* condition 1 */)
{
}
else if (/* condition 2 */)
{
}

// Équivaut à ceci.

if (/* condition 1 */)
{
}
else
{
    if (/* condition 2 */)
    {
    }
}

[T.P] Gérer les erreurs d'entrée — Partie I

Vous souvenez-vous de la problématique du chapitre précédent ? Nous avions des problèmes quand l’utilisateur rentrait du texte à la place d’un entier, par exemple. Eh bien nous pouvons déjà apporter une première solution à ce problème, solution que nous améliorerons au fur et à mesure.

Les développeurs ayant écrit la bibliothèque standard C++ ont déjà tout prévu. Trois fonctions, déjà programmées, vont nous intéresser.

  • std::cin >> x est en fait une fonction qui renvoie true si tout est correct ou false si on a rencontré une erreur lors de la saisie.
  • std::cin.clear() restaure std::cin à un état fonctionnel, sans erreur.
  • std::cin.ignore() permet d’ignorer un nombre défini de caractères, soit jusqu’à un nombre maximum (exemple 500), soit jusqu’à un caractère précis (exemple '\n' ou 'a'). Dans notre cas, nous allons utiliser ceci :
std::cin.ignore(255, '\n');

Maintenant, vous avez toutes les informations nécessaires pour commencer la protection de nos entrées. À vous de jouer !

Correction T.P Partie I
#include <iostream>
#include <string>

int main()
{
    std::cout << "Entre ton age : ";
    unsigned int age { 0 };
    
    if (std::cin >> age)
    {
        // Tout va bien.
        std::cout << "Tu as " << age << " ans.\n";
    }
    else
    {
        // Si std::cin a rencontré une erreur quelconque.
        std::cout << "Tu n'as pas rentré un entier, il y a eu une erreur." << std::endl;
        std::cin.clear(); // On remet std::cin dans un état fonctionnel.
        std::cin.ignore(255, '\n'); // On vide les caractères mémorisés.
    }

    std::cout << "Maintenant, vérifions que tout va bien." << std::endl;
    std::cout << "Entre ton nom : ";
    std::string nom { "" };
    std::cin >> nom;
    std::cout << "Tu t'appelles " << nom << ".\n";

    return 0;
}

Si vous avez eu du mal ou n’avez pas trouvé, ce n’est pas grave, vous êtes en train d’apprendre. N’hésitez pas à relire la solution jusqu’à ce que tout vous paraisse limpide.

Voilà, nous avons déjà une petite protection. Mais elle ne marche qu’une fois : impossible de la faire marcher tant que l’utilisateur n’a pas rentré quelque chose de valide. Pour l’instant, laissez ça de côté, nous en reparlerons dans le prochain chapitre.

La logique booléenne

Nous avons vu les différents moyens de tester des conditions, ainsi que différents opérateurs de comparaisons qui retournent des booléens. Mais ce ne sont pas les seuls. Cette section va introduire l’algèbre booléenne, et rassurez-vous, c’est simple à comprendre, puissant et bien utile.

AND — Tester si deux conditions sont vraies

Imaginons un instant que vous souhaitiez faire l’acquisition d’une voiture. Pour cela, il vous faut remplir deux conditions : avoir le permis et suffisamment d’argent. Si l’une des deux conditions n’est pas remplie, ou les deux, vous continuerez à prendre le bus. Le seul moyen de faire hurler le moteur sur l’autoroute, c’est d’avoir assez d’argent ET d’avoir le permis.

C++ nous fournit deux moyens d’exprimer cet opérateur logique « ET » : && et and. Les deux sont parfaitement valables et interchangeables. Le deuxième était notamment prévu à l’époque où tous les claviers ne possédaient pas le symbole &.

Ancienne spécificité Microsoft

Si and ne marche pas avec Visual Studio, il faut inclure le fichier <ciso646>. Avec une version à jour, ce n’est cependant plus nécessaire.

#include <iostream>

int main()
{
    int const prix_voiture { 5000 };
    int const argent { 2000 };
    bool const a_le_permis { true };

    if (argent >= prix_voiture && a_le_permis)
    {
        std::cout << "Voici les clés, bonne route." << std::endl;
    }
    else
    {
        std::cout << "Désolé, vous allez devoir prendre le bus." << std::endl;
    }

    return 0;
}

Testez donc ce programme en modifiant le prix de la voiture, l’argent disponible ou la possession ou non du permis. Comme écrit plus haut, il n’y a que si a_le_permis est à true et qu’il y a plus d’argent que le prix de la voiture que vous serez en mesure d’avoir les clés.

Voici ce qu’on appelle la table de vérité de l’opérateur AND, qui formalise les entrées et les sorties de cet opérateur. N’hésitez pas à en lire plus sur le sujet s’il vous intéresse. :)

Première condition Seconde condition Résultat de l’opérateur « AND »
false false false
false true false
true false false
true true true

OR — Tester si au moins une condition est vraie

Maintenant, nous allons aller visiter un musée. Quand on arrive devant l’entrée, bonne surprise, l’entrée est gratuite si l’on a moins de 26 ans OU si l’on est professeur (peu importe la matière). Il suffit de remplir l’une des deux conditions pour être admis gratuitement à l’intérieur. Bien sûr, si vous remplissez les deux, l’offre reste valable.

Cet exemple va servir de base pour l’introduction de l’opérateur C++ « OU » : au choix, || (qui se tape sur un AZERTY français en faisant Alt Gr + - (le numéro 6) ou or, présent pour la même raison que précédemment.

Ancienne spécificité Microsoft

Si or ne marche pas avec Visual Studio, il faut inclure le fichier <ciso646>. Avec une version à jour, ce n’est cependant plus nécessaire.

#include <iostream>

int main()
{
    int const age { 25 };
    bool const est_professeur { true };

    if (age <= 26 || est_professeur)
    {
        std::cout << "Bonne visite." << std::endl;
    }
    else
    {
        std::cout << "Désolé, il va falloir payer." << std::endl;
    }

    return 0;
}

Encore une fois, faites vôtre ce programme et modifiez-le. Voyez dans quelles circonstances l’entrée est gratuite et à quels moments il faut payer. Vous allez obtenir des résultats identiques à la table de vérité ci-dessous.

Première condition Seconde condition Résultat de l’opérateur « OR »
false false false
false true true
true false true
true true true

NOT — Tester la négation

Il fait chaud, vous allez à la piscine vous rafraichir. En arrivant, vous jetez un œil au règlement et vous voyez qu’il est interdit de nager si l’on a un short de bain et non un maillot de bain. Vous raisonnez donc ainsi : « Si j’ai un maillot de bain, alors tout va bien, l’entrée m’est autorisée, je peux nager. Si j’ai un short de bain, alors l’entrée m’est interdite et je dois en acheter un avant de nager. » Comment exprimeriez-vous ça en C++ ? Peut-être avec le code ci-dessous ?

int main()
{
    bool const ai_je_un_maillot { true };
    if (ai_je_un_maillot)
    {
        // Je n'ai rien à faire, tout est OK.
    }
    else
    {
        // Je dois aller acheter un maillot.
    }

    // Je nage et je suis heureux.
    return 0;
}

Ce code fonctionne, mais il nous pose un problème : on a un if vide et c’est moche. L’idéal serait de ne traiter que le cas « Pas de maillot de bain ». C++ nous permet justement cela avec l’opérateur de négation ! ou not. Celui-ci renvoie la valeur inverse de l’expression testée. Si l’expression vaut true, alors cet opérateur renverra false ; si, au contraire, elle valait false, sa négation donnera true.

Ancienne spécificité Microsoft

Si not ne marche pas avec Visual Studio, il faut inclure le fichier <ciso646>. Avec une version à jour, ce n’est cependant plus nécessaire.

int main()
{
    bool const ai_je_un_maillot { true };
    if (!ai_je_un_maillot)
    {
        // Je dois aller acheter un maillot.
    }

    // Je nage et je suis heureux.
    return 0;
}

Notre code devient plus court, plus concis et plus agréable à lire, il gagne en qualité. Je vous mets en dessous la table de vérité, bien plus courte cette fois.

Valeur de la condition Résultat de l’opérateur « NOT »
true false
false true

Il y a juste quelque chose que je ne comprends pas. Pourquoi ne pas créer tout simplement un booléen inverse, genre pas_de_maillot ? Pourquoi se casser la tête à tester la négation d’une condition et ne pas tester la condition inverse tout de suite ?

C’est une très bonne question. Dans un exemple aussi simpliste que celui-ci, monté de toute pièce, on aurait pu, c’est vrai. Mais d’autres fois nous n’avons pas le choix, car nous utilisons du code qui a été programmé d’une certaine façon.

Tester plusieurs expressions

Nous ne sommes absolument pas limités à tester deux expressions en même temps. On peut le faire avec trois, quatre, dix, soixante (bien que soixante soit un exemple extrême et vraiment trop peu lisible). Le code suivant le prouve.

#include <iostream>

int main()
{
    bool const ai_je_envie_de_nager{ true };
    bool const ai_je_un_maillot { true };
    int const argent { 20 };
    int const prix { 5 };

    if (ai_je_envie_de_nager && ai_je_un_maillot && argent >= prix)
    {
        std::cout << "PLOUF !" << std::endl;
    }
    else
    {
        std::cout << "Huum, un autre jour." << std::endl;
    }
    
    return 0;
}

Dans le cas du même opérateur, ils sont évalués de gauche à droite. C’est le cas du code précédent. Mais que se passe t-il si l’on mélange plusieurs opérateurs différents dans la même expression ? Sauriez-vous dire ce que quelque chose comme expression1 && expression 2 || expression3 va donner ? En effet, les opérateurs logiques sont comme les opérateurs mathématiques que nous avons vus dans les chapitres précédents : ils ont une priorité.

  1. Le plus prioritaire, c’est la négation !.
  2. Ensuite vient le « ET » &&.
  3. Enfin, le « OU » || est le moins prioritaire.

Ainsi, dans l’exemple !a && b, c’est d’abord !a qui est évalué, puis la nouvelle valeur et b sont testées avec l’opérateur ET &&. Avec le code a && b || c && d, dans l’ordre, on évalue a && b, c && d et enfin a && b || c && d.

Bien que la priorité des opérateurs soit clairement définie par C++, une bonne pratique consiste à ajouter des parenthèses autour des expressions pour rendre le code plus lisible et plus clair. Si l’on reprend nos exemples, cela donne (!a) && b pour le premier et (a && b) || (c && d) pour le deuxième. C’est clair ? Prouvez-le-moi en mettant les parenthèses au bon endroit dans ces codes (piqués à @gbdivers).

a && b && c
a || b || c
a || b && c
a && b || c
 
!a && b
a || !b
 
a && b || c && d
a || b && c || d
Correction

Voici la solution. L’avez-vous trouvée ? Vous êtes sur la bonne voie ! Vous avez-eu du mal ? N’hésitez pas à relire les passages précédents à tête reposée et à faire vos propres exemples.

(a && b) && c
(a || b) || c
a || (b && c)
(a && b) || c
 
(!a) && b
a || (!b)
 
(a && b) || (c && d)
(a || (b && c)) || d
Parenthèses

À partir de maintenant, je vais mettre des parenthèses dans le code pour le rendre clair et explicite.

Évaluation en court-circuit

Imaginez un formulaire à remplir, avec de multiples questions, pour demander une place de parking dans votre immeuble. Pour ça, il faut déjà habiter l’immeuble en question et avoir une voiture. Si vous habitez ailleurs, pas la peine de réfléchir à la possession ou non d’une voiture, vous êtes inéligible pour cette place de parking.

Eh bien le compilateur mène le même raisonnement quand il évalue une expression constituée de « AND ». S’il détecte qu’une condition est fausse, alors il ne sert à rien d’évaluer le reste de l’expression puisque le résultat est forcément faux. Si vous ne voyez pas pourquoi, retournez un peu plus haut jeter un œil à la table de vérité de « AND ». ;)

#include <iostream>

int main()
{
    bool const ai_je_une_voiture { false };
    bool const habite_immeuble { true };

    // Comme ai_je_une_voiture vaut false, le compilateur ne va pas tester l'expression habite_immeuble.
    if (ai_je_une_voiture && habite_immeuble)
    {
        std::cout << "Voici votre place de parking." << std::endl;
    }
    else
    {
        std::cout << "Désolé, vous n'êtes pas éligible." << std::endl;
    }
    
    return 0;
}

Avec l’opérateur « OR », le même principe s’applique : si l’une des expressions est évaluée à true, alors, selon la table de vérité de l’opérateur « OR », le résultat sera true, donc pas la peine d’évaluer le reste.

Ce principe d’optimisation par le compilateur s’appelle l’évaluation en court-circuit. Il permet d’optimiser l’exécution du code en ne perdant pas de temps sur des instructions qui seront forcément évaluées à false ou à true. Quand nous avancerons dans notre apprentissage du C++, nous verrons des cas où l’évaluation en court-circuit est bien utile.

Exercices

Allez, c’est l’heure de pratiquer un peu. C’est le meilleur moyen de progresser et de fixer toutes ces nouvelles notions dans votre mémoire. N’hésitez pas à faire cette section à tête reposée et avec le cours sous les yeux en même temps.

Un mot sur la licence

Certains exercices sont adaptés du cours sur le langage C, comme m’y autorisent la licence et mon statut de double auteur. :-°

Une pseudo-horloge

Demandons à l’utilisateur de rentrer un nombre et nous lui afficherons la période de la journée correspondante : nuit, matin, après-midi, soir. Ainsi, entre 8h et 12h, nous sommes le matin. Mais attention, nous voulons aussi gérer les cas un peu spéciaux que sont midi, minuit et un nombre qui n’est pas entre 0 et 24.

Correction pseudo-horloge
#include <iostream>

int main()
{
    std::cout << "Quelle heure est-il ?" << std::endl;
    int heure { 0 };
    std::cin >> heure;

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

    return 0;
}

Score

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 ! »
Correction score
#include <iostream>

int main()
{
    std::cout << "Quel est le score du joueur ? " << std::endl;
    int score { 0 };
    std::cin >> score;

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

    return 0;
}

XOR — Le OU exclusif

Le OR que nous avons vu plus tôt dans ce chapitre est dit « inclusif ». Ainsi, a || b signifie a, ou b, ou les deux. Le but de cet exercice va être de faire un OU dit « exclusif », c’est-à-dire que si a et b renvoient la même chose, l’expression est évaluée à false, sinon à true. Vous pouvez le faire avec les opérateurs que nous avons vus.

Indice

Si vous bloquez, voici déjà la table de vérité de l’opérateur.

Première condition Seconde condition Résultat de l’opérateur « XOR »
false false false
false true true
true false true
true true false
Correction XOR

La solution s’écrit ainsi : (a && !b) || (b && !a). Vous pouvez vérifier avec le programme ci-dessous.

#include <iostream>

int main()
{
    std::cout << std::boolalpha;

    bool const deux_false { (false && !false) || (false && !false) };
    std::cout << "(false && !false) || (false && !false) == " << deux_false << std::endl;

    bool const true_false { (true && !false) || (false && !true) };
    std::cout << "(true && !false) || (false && !true) == " << true_false << std::endl;

    bool const false_true { (false && !true) || (true && !false) };
    std::cout << "(false && !true) || (true && !false) == " << false_true << std::endl;

    bool const deux_true { (true && !true) || (true && !true) };
    std::cout << "(true && !true) || (true && !true) == " << deux_true << std::endl;
    
    return 0;
}

Sinon vous pouvez utiliser xor, si vous incluez bien <ciso646> avec Visual Studio. :)


En résumé

  • Nous pouvons tester une expression avec if. Si celle-ci est fausse, on peut tester d’autres expressions avec autant de else if qu’on veut. Enfin, else permet de tester le cas général, « tout le reste ».
  • Nous pouvons comparer des expressions entre elles avec les opérateurs de comparaisons.
  • Les opérateurs logiques permettent de tester plusieurs expressions en même temps ou d’en obtenir la négation.
  • Le compilateur optimise l’évaluation de ces multiples expressions grâce à l’évaluation en court-circuit.