Information

Deadline Geen deadline
Submission limit No limitation

Tags

Sign in

[3.1] Rappel des concepts [Obligatoire]

La sélection

Le but d'une sélection est de changer le comportement d'un programme en fonction d'une condition qui est une expression booléenne.

Par exemple, voici un petit programme qui affiche des messages différents en fonction de si un nombre entré par l'utilisateur est positif ou négatif :

#include <stdio.h>

int main(void) {
    int number;
    scanf("%d", &number);

    if (number >= 0) {
        printf("Le nombre est positif.\n");
    } else {
        printf("Le nombre est négatif.\n");
    }

    return 0;
}

Dans ce programme, on voit bien les 3 parties de la sélection : la condition number >= 0, la partie "if" (ou "then") qui contient le code à exécuter si la condition s'évalue à true et la partie "else" qui contient le code à exécuter si la condition s'évalue à false. Il est aussi possible d'ajouter d'autres conditions avec l'usage de "else if" comme dans l'exemple suivant :

#include <stdio.h>

int main(void) {
    int number;
    scanf("%d", &number);

    if (number == 0) {
        printf("Le nombre est zéro.\n");
    } else if (number > 0) {
        printf("Le nombre est strictement positif.\n");
    } else {
        printf("Le nombre est strictement négatif.\n");
    }

    return 0;
}

Bonnes pratiques

Réorganisation des conditions

Il existe plusieurs mécanismes qui permettent de simplifier ou de réduire le nombre de conditions dans une sélection en les réorganisant.

Short-circuit evaluation

La short-circuit evaluation est un comportement spécial des opérateurs and (&&) et or (||). Il permet de n'évaluer que ce qui est nécessaire pour pouvoir dire si une expression est true ou false. Par exemple, dans l'expression x == 0 && y > 0, on peut directement savoir que l'expression sera évaluée à false si x est différent de 0 sans avoir besoin d'évaluer y > 0. C'est pareil pour l'expression a == 0 || b > 0 quand a est égal à 0. On peut directement savoir que l'expression sera évaluée à true sans avoir besoin d'évaluer b > 0.

C'est un comportement très pratique quand on veut faire des opérations qui pourraient faire crasher le programme si elles ne sont pas vérifiées, comme dans l'exemple suivant :

#include <stdio.h>

int main(void) {
    int diviseur;
    scanf("%d", &diviseur);

    if (diviseur != 0 && 10 % diviseur == 0) {
        printf("10 est divisible par %d.\n", diviseur);
    } else {
        printf("10 n'est pas divisible par ce nombre ou le diviseur est 0.\n");
    }

    return 0;
}

Ici, l'opération 10 % diviseur pourrait faire crasher le programme si la variable diviseur était égale à 0 mais puisqu'il y a la condition diviseur != 0 avant et qu'il y a le mécanisme de short-circuit evaluation, cela est impossible.

Changement de l'ordre des "else if"

L'ordre des "else if" a de l'importance car ils sont évalués dans l'ordre d'écriture. Cela permet de ne pas répéter des conditions qui ne peuvent déjà plus être vraies comme dans l'exemple suivant :

#include <stdio.h>

int main(void) {
    int age;
    scanf("%d", &age);

    if (age >= 18) {
        printf("Tu es adulte.\n");
    } else if (age >= 12) {  // déjà équivalent à "age < 18 && age >= 12" dans ce contexte donc pas besoin d'ajouter "age < 18"
        printf("Tu es adolescent.\n");
    } else {
        printf("Tu es enfant.\n");
    }

    return 0;
}

Factorisation

La factorisation est une pratique visant à identifier et supprimer les bouts de code répétés dans un programme. Dans cette section, nous allons en parler dans le contexte des sélections mais elle peut aussi s'appliquer dans d'autres contextes. Prenez par exemple le code suivant :

#include <stdio.h>

int main(void) {
    double salaire, bonus;
    int annee_anciennete;
    scanf("%d", &annee_anciennete);

    salaire = 2000.0;

    if (annee_anciennete < 3) {
        bonus = 0;
        printf("Bonus attribué : %.2f\n", bonus);
        printf("Nouveau salaire : %.2f\n", salaire + bonus);
    } else if (annee_anciennete < 5) {
        bonus = salaire * 0.05;
        printf("Bonus attribué : %.2f\n", bonus);
        printf("Nouveau salaire : %.2f\n", salaire + bonus);
    } else {
        bonus = salaire * 0.10;
        printf("Bonus attribué : %.2f\n", bonus);
        printf("Nouveau salaire : %.2f\n", salaire + bonus);
    }

    return 0;
}

Dans cet exemple, on peut voir que les mêmes printf sont répétés dans chaque partie de la sélection. De plus, il est possible de les extraire de la sélection sans que cela ne change le comportement du programme. Grâce à cela, on peut supprimer le code dupliqué, le rendre plus facile à comprendre et plus facilement modifiable :

#include <stdio.h>

int main(void) {
    double salaire, bonus;
    int annee_anciennete;
    scanf("%d", &annee_anciennete);

    salaire = 2000.0;

    if (annee_anciennete < 3) {
        bonus = 0;
    } else if (annee_anciennete < 5) {
        bonus = salaire * 0.05;
    } else {
        bonus = salaire * 0.10;
    }

    printf("Bonus attribué : %.2f\n", bonus);
    printf("Nouveau salaire : %.2f\n", salaire + bonus);

    return 0;
}

Simplification des expressions booléennes

Il est possible de simplifier les expressions booléennes des conditions grâce aux lois de De Morgan afin de les rendre plus lisibles. Par exemple, l'expression !(a && b) est équivalente à !a || !b et l'expression !(a || b) est équivalente à !a && !b.

Il est aussi possible de simplifier l'assignation d'une variable booléenne dans une sélection en assignant directement le résultat de la condition à la variable. Par exemple, au lieu d'écrire :

bool est_adulte;
int age;
scanf("%d", &age);

if (age >= 18) {
    est_adulte = true;
} else {
    est_adulte = false;
}

On peut écrire plus simplement :

bool est_adulte;
int age;
scanf("%d", &age);

est_adulte = (age >= 18);

Le switch case

Le switch case est un autre type de sélection qui est très optimisé mais qui ne marche que sur une seule expression. Elle permet d'exécuter du code en fonction de valeurs spécifiques, par exemple :

#include <stdio.h>

int main(void) {
    int jour;
    scanf("%d", &jour);

    switch (jour) {
        case 1:
            printf("Lundi\n");
            break;
        case 2:
            printf("Mardi\n");
            break;
        case 3:
            printf("Mercredi\n");
            break;
        case 4:
            printf("Jeudi\n");
            break;
        case 5:
            printf("Vendredi\n");
            break;
        case 6:
            printf("Samedi\n");
            break;
        case 7:
            printf("Dimanche\n");
            break;
        default:
            printf("Jour invalide\n");
            break;
    }

    return 0;
}

Comme vous pouvez le voir, un printf différent est à chaque fois exécuté en fonction des valeurs 1, 2, 3, 4, 5, 6, 7 et un cas default est exécuté dans le reste des cas.

À noter que les instructions break sont importantes dans un switch case car, sans elles, les cases d'après seraient à chaque fois exécutés comme dans l'exemple ci-dessous :

int main(void) {
    int jour;
    scanf("%d", &jour);

    switch (jour) {
        case 1:
            printf("Lundi\n");
        case 2:
            printf("Mardi\n");  // Il manque le break à la fin du case précédent donc "Mardi" sera aussi affiché si jour est égal à 1
            break;
        case 3:
            printf("Mercredi\n");
            break;
        case 4:
            printf("Jeudi\n");
            break;
        case 5:
            printf("Vendredi\n");
            break;
        case 6:
            printf("Samedi\n");
            break;
        case 7:
            printf("Dimanche\n");
            break;
        default:
            printf("Jour invalide\n");
            break;
    }

    return 0;
}

Question 1: Éléments d'une sélection
#include <stdio.h>

#define PI 3.14

int main(void) {
    float rayon;
    scanf("%f", &rayon);

    if (rayon >= 0) {
        printf("Surface: %f\n Circonference: %f\n", PI * rayon * rayon, 2 * PI * rayon);
    } else {
        printf("Erreur: le rayon ne peut pas etre negatif.\n");
    }

    return 0;
}

Étant donné le programme ci-dessus, associez chaque élément présent dans la sélection avec le nom qui lui correspond :

Question 2: Condition

Dans une sélection de type if...else, il y a toujours une condition:

if (<CONDITION>) {
  ...
} else {
  ...
}

Cette condition est en réalité une expression. Mais quel est le type de cette expression ? On veut le nom du type en C. Par exemple, si tu penses qu'une condition est un caractère, tu ne dois pas écrire "caractère" mais char. Voir les rappels ou la partie 4.1 du syllabus !

Question 3: Qu'est-ce qui pourrait être une condition ?

Cochez ce qui pourrait constituer une condition dans une sélection sachant que :

  • La variable a est de type int
  • la variable b est de type bool
  • la variable c est de type char
Question 4: Complétez ce programme
#include <stdio.h>

int main(void) {
    char devise;
    printf("Entrez la devise que vous souhaitez convertir (e pour convertir de Euro à Dong vietnamien, d pour convertir de Dong à Euro): ");
    scanf("%c",&devise);

    1️⃣ (devise 2️⃣ 'e' || devise == 'd') {
        float taux;
        3️⃣ (devise == 'e') {
            taux = 26798.59;
            printf("");
        } 4️⃣ {
            taux = 0.000037315400211861357;
        }

        printf("Entrez une quantité d'argent que vous souhaitez convertir: ");
        float argent = read_float();

        printf("Conversion: %f\n", argent * taux);
    } 5️⃣ {
        printf("Devise incorrecte !\n");
    }

    return 0;
}

Étant donné le programme ci-dessus, associez chaque élément présent avec des if, des else ou des == :

Question 5: Différentes formes de sélection

Cochez les bouts de programme syntaxiquement corrects :

Question 6: Qu'affiche le programme ?
#include <stdio.h>

int main(void) {
    int a, b;
    scanf("%i %i", &a, &b);

    if (a <= 0 || a > 12) {
        printf("0\n");
    } else if (a == 2 &&  (b % 4 == 0 || b % 100 == 0) && b % 400 != 0) {
        printf("1\n");
    } else if (a == 2) {
        printf("2\n");
    } else if ((a <= 7 && a % 2 == 1) || (a > 7 && a % 2 == 0)) {
        printf("3\n");
    } else if((a <= 7 && a % 2 == 0 && a != 2) || (a > 7 && a % 2 == 1)) {
        printf("4\n");
    }

    return 0;
}

Vous remarquez que ce programme permet à utilisateur d'entrer deux entiers. Voici donc une liste d'entiers entrés par l'utilisateur. Pour chacun d'entre eux, déterminez ce que le programme affiche à l'écran.

Réalisez cet exercice uniquement en lisant le programme ! N'essayez pas de l'exécuter !

Question 7: Il fait quoi ce programme ?
#include <stdio.h>

int main(void) {
    int a, b;
    scanf("%i %i", &a, &b);

    if (a <= 0 || a > 12) {
        printf("0");
    } else if (a == 2 && (b % 4 == 0 || b % 100 == 0) && b % 400 != 0) {
        printf("29");
    } else if (a == 2){
        printf("28");
    } else if ((a <= 7 && a % 2 == 1) || (a > 7 && a % 2 == 0)) {
        printf("31");
    } else if ((a <= 7 && a % 2 == 0 && a != 2) || (a > 7 && a % 2 == 1)) {
        printf("30");
    }

    return 0;
}

Maintenant, on remarque que ce programme calcule le 1️⃣ de 2️⃣ d'un 3️⃣ entré par l'utilisateur, étant donné une 4️⃣ aussi entrée par l'utilisateur.

Question 8: Organisation des conditions

Cochez les bouts de programme qui possèdent des conditions qui pourraient être supprimées ou simplifiées :

Question 9: Factorisation

Cochez les bouts de programme qui peuvent être factorisés :

Question 10: Switch case
switch (x) {
    case 0:
        printf("Succès");
        break;
    case 1:
        printf("Erreur");
        break;
    default:
        printf("Code invalide");
        break;
}

Qu'est-ce qui est affiché après l'exécution du bout de code ci-dessus si x vaut 1 ?