Une valeur, plusieurs constantes
Une chaîne else if qui compare sans cesse la même variable à des constantes est un motif qui a son instruction dédiée en C :
if (choice == 1) { /* ... */ }
else if (choice == 2) { /* ... */ }
else if (choice == 3) { /* ... */ }
else { /* ... */ }
switch le dit plus directement. Il évalue l'expression une fois, puis saute droit à l'étiquette correspondante.
Changez choice et relancez. La structure rend l'ensemble des valeurs légales visible d'un coup d'œil, ce qui est la vraie raison de la préférer ici.
Syntaxe et règles sur les étiquettes case
switch (expressionEntiere) {
case CONSTANTE_1:
instructions
break;
case CONSTANTE_2:
instructions
break;
default:
instructions
break;
}
Quatre contraintes que le C impose :
- L'expression du switch doit avoir un type entier -
int,char,short,long, ou uneenum. Pasfloat, pasdouble, et pas une chaîne. - Chaque étiquette
casedoit être une expression constante connue à la compilation.case n:avecnune variable est une erreur ;case 3 + 4:etcase MAX:(un#defineou une constante d'enum) conviennent. - Deux cas ne peuvent pas avoir la même valeur. Les doublons sont une erreur de compilation, un filet de sécurité modeste mais réel.
- Un cas ne peut pas exprimer une plage.
case 1 ... 5:est une extension GCC, pas du C standard ; utilisez unifpour les plages.
default est facultatif et peut apparaître n'importe où dans le bloc, même si le placer en dernier est la convention quasi universelle. Sans default, une valeur sans correspondance saute simplement tout le switch.
break : pourquoi il n'est pas optionnel
La chose la plus surprenante à propos de switch en C, c'est que les étiquettes case sont des cibles de saut, pas des boîtes. Une fois que l'exécution atterrit sur une étiquette, elle poursuit à travers chaque instruction en dessous - par-delà les autres étiquettes case - jusqu'à un break ou l'accolade fermante.
Le premier switch affiche un, deux et trois - il a trouvé case 1 puis a exécuté tout ce qui suit. Le second n'affiche que un. Ce comportement s'appelle le passage au cas suivant, et oublier un break est le bug classique du switch en C : le programme est silencieusement faux plutôt que de refuser de compiler.
Compilez avec gcc -Wall -Wextra -Wimplicit-fallthrough et le compilateur pointe chaque cas qui tombe dans le suivant.
Le break du dernier cas est techniquement redondant - le bloc s'y termine de toute façon. Écrivez-le quand même, pour qu'ajouter un nouveau cas en dessous plus tard ne crée pas discrètement un passage au suivant.
Le passage au cas suivant délibéré
Le passage au cas suivant est une fonctionnalité quand il est intentionnel. Empiler des étiquettes sans instructions entre elles est la façon ordinaire de dire « ces valeurs font la même chose » :
Trois étiquettes partagent un corps, sans code dupliqué ni longue condition ||. Les compilateurs n'avertissent pas sur cette forme, car il n'y a aucune instruction à traverser.
Le passage au suivant avec des instructions entre les deux - où le cas 1 s'exécute et continue ensuite délibérément dans le cas 2 - est plus rare et mérite un commentaire explicite, puisqu'il faut dire au lecteur que le break manquant était une décision :
switch (level) {
case 3:
printf("verbeux\n");
/* fall through */
case 2:
printf("info\n");
/* fall through */
case 1:
printf("erreurs\n");
break;
}
/* fall through */ est le commentaire que l'avertissement de GCC reconnaît, il fait donc aussi taire le diagnostic.
Switcher sur des caractères
Parce que char est un type entier, la classification de caractères s'y prête naturellement :
Notez que case 'a': utilise des apostrophes - une constante caractère, qui n'est que la valeur entière de ce caractère. case "a": serait une chaîne et ne compilera pas.
Switcher sur des enums
Le meilleur appariement en C est switch avec une enum. Les étiquettes de cas deviennent des noms lisibles, et les compilateurs peuvent avertir quand le switch ne traite pas tous les énumérateurs :
Deux choses à copier ici. Chaque cas fait return, donc aucun break n'est nécessaire - un return quitte la fonction entièrement, ce qui termine aussi le switch. Et il n'y a pas de default : avec -Wswitch (inclus dans -Wall), omettre le default fait avertir le compilateur le jour où quelqu'un ajoute un cinquième état et oublie cette fonction. Un default aurait caché cela.
Déclarer des variables dans un switch
Un corps de switch est un seul bloc, donc une déclaration dans un cas est visible - quoique pas forcément initialisée - dans ceux qui suivent. Sauter par-dessus une initialisation est une erreur en C :
switch (n) {
case 1:
int x = 10; // erreur en C : une etiquette ne peut preceder une declaration ainsi
printf("%d\n", x);
break;
case 2:
printf("%d\n", x); // x existe ici, mais n'a jamais ete initialise
break;
}
Entourez le cas de ses propres accolades quand il a besoin de variables locales :
switch (n) {
case 1: {
int x = 10;
printf("%d\n", x);
break;
}
case 2:
printf("deux\n");
break;
}
switch ou if-else ?
Prenez switch quand tout ceci est vrai : vous testez une seule expression, elle a un type entier, et vous la comparez à des constantes fixes. La gestion de menus, les machines à états sur enum, la classification de caractères et le dispatch de commandes sont les cas canoniques. Le bénéfice est la lisibilité, la vérification des valeurs en double, et un compilateur qui peut avertir des énumérateurs non traités.
Prenez if-else quand les tests impliquent des plages (score >= 90), des valeurs flottantes, des chaînes (il vous faut strcmp), plusieurs variables différentes, ou des conditions combinées avec && et ||. Aucune de celles-ci ne peut s'exprimer en étiquette de cas.
La performance est rarement le facteur décisif. Les compilateurs transforment un switch dense en table de saut et un switch épars en comparaisons, donc sur une poignée de cas les deux formes sont indiscernables. Choisissez celle qui exprime l'intention.
Questions fréquentes
Comment fonctionne une instruction switch en C ?
switch (expr) évalue une expression entière une fois, saute à l'étiquette case dont la constante correspond, et s'exécute à partir de là jusqu'à rencontrer un break ou la fin du switch. Si rien ne correspond, il saute à default s'il y en a un, et sinon saute tout le bloc.
Pourquoi ai-je besoin de break dans un switch C ?
Parce que les étiquettes case sont des cibles de saut, pas des blocs séparés. Sans break, l'exécution continue directement dans les instructions du cas suivant - c'est le passage au cas suivant. Un break manquant est le bug de switch le plus courant en C, et gcc -Wimplicit-fallthrough le signalera.
Peut-on faire un switch sur une chaîne en C ?
Non. Un switch C exige une expression entière, donc les valeurs int, char et enum fonctionnent, mais pas les chaînes, les flottants ni les plages. Pour brancher sur une chaîne, utilisez strcmp dans une chaîne if-else, ou associez d'abord la chaîne à une enum et switchez dessus.
Quand utiliser switch plutôt que if-else en C ?
Utilisez switch quand vous comparez un entier ou un caractère à une liste de constantes fixes - commandes de menu, états d'enum, classification de caractères. Utilisez une chaîne if-else quand les tests impliquent des plages, plusieurs variables, ou autre chose qu'une égalité avec une constante de compilation.