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;
}
INGInious