Considérez la switch
déclaration suivante :
switch( value )
{
case 1:
return 1;
default:
value++;
// fall-through
case 2:
return value * 2;
}
Ce code se compile, mais est-il valide (= comportement défini) pour C90 / C99? Je n'ai jamais vu de code où le cas par défaut n'est pas le dernier cas.
EDIT:
Comme l' écrivent Jon Cage et KillianDS : c'est un code vraiment laid et déroutant et j'en suis bien conscient. Je suis juste intéressé par la syntaxe générale (est-elle définie?) Et le résultat attendu.
c
switch-statement
Tanascius
la source
la source
goto
n'est pas mal. Les adeptes du culte du fret le sont! Vous ne pouvez pas imaginer les extrêmes que les gens peuvent évitergoto
parce que c'est prétendument si maléfique, ce qui crée un véritable gâchis illisible de leur code.goto
principalement pour simuler quelque chose comme unefinally
clause dans les fonctions, où les ressources (fichiers, mémoire) doivent être libérées lors de l'arrêt, et répéter pour chaque cas d'erreur une liste defree
etclose
n'aide pas à la lisibilité. Il y a bien une utilisationgoto
que j'aimerais éviter mais que je ne peux pas, c'est quand je veux sortir d'une boucle et que je suis dansswitch
cette boucle.Réponses:
La norme C99 n'est pas explicite à ce sujet, mais en prenant tous les faits ensemble, elle est parfaitement valable.
A
case
etdefault
label sont équivalents à ungoto
label. Voir 6.8.1 Déclarations étiquetées. Particulièrement intéressant est 6.8.1.4, qui active le dispositif de Duff déjà mentionné:Edit : Le code dans un commutateur n'a rien de spécial; c'est un bloc de code normal comme dans une
if
instruction-, avec des étiquettes de saut supplémentaires. Cela explique le comportement fall-through et pourquoibreak
est nécessaire.6.8.4.2.7 donne même un exemple:
Les constantes de cas doivent être uniques dans une instruction switch:
Tous les cas sont évalués, puis il passe à l'étiquette par défaut, si elle est donnée:
la source
default
cas dominant les autres cas d'environ 100: 1, et je ne sais pas s'il est valide ou indéfini pour fairedefault
le premier cas.Les instructions case et l'instruction par défaut peuvent se produire dans n'importe quel ordre dans l'instruction switch. La clause par défaut est une clause facultative qui correspond si aucune des constantes des instructions case ne peut être mise en correspondance.
Bon exemple :-
très utile si vous voulez que vos cas soient présentés dans un ordre logique dans le code (comme dans, sans dire cas 1, cas 3, cas 2 / par défaut) et que vos cas sont très longs donc vous ne voulez pas répéter le cas entier code en bas pour la valeur par défaut
la source
C'est valable et très utile dans certains cas.
Considérez le code suivant:
Le fait est que le code ci-dessus est plus lisible et plus efficace qu'en cascade
if
. Vous pourriez mettredefault
à la fin, mais cela ne sert à rien car cela concentrera votre attention sur les cas d'erreur au lieu des cas normaux (ce qui est ledefault
cas ici ).En fait, ce n'est pas un si bon exemple, car
poll
vous savez combien d'événements peuvent se produire au maximum. Mon vrai point est qu'il existe des cas avec un ensemble défini de valeurs d'entrée où il y a des «exceptions» et des cas normaux. S'il vaut mieux placer des exceptions ou des cas normaux au premier plan, c'est une question de choix.Dans le domaine du logiciel, je pense à un autre cas très courant: les récursions avec certaines valeurs terminales. Si vous pouvez l'exprimer à l'aide d'un commutateur,
default
sera la valeur habituelle qui contient l'appel récursif et les éléments distingués (cas individuels) les valeurs du terminal. Il n'est généralement pas nécessaire de se concentrer sur les valeurs terminales.Une autre raison est que l'ordre des cas peut changer le comportement du code compilé, ce qui compte pour les performances. La plupart des compilateurs génèrent du code d'assemblage compilé dans le même ordre que le code apparaît dans le commutateur. Cela rend le premier cas très différent des autres: tous les cas sauf le premier impliqueront un saut et cela videra les pipelines du processeur. Vous pouvez le comprendre comme un prédicteur de branche exécutant par défaut le premier cas apparaissant dans le commutateur. Si un cas est beaucoup plus courant que les autres, vous avez de très bonnes raisons de le mettre comme premier cas.
La lecture des commentaires est la raison spécifique pour laquelle l'affiche originale a posé cette question après avoir lu la réorganisation de la boucle de branche du compilateur Intel sur l'optimisation du code.
Ensuite, cela deviendra un arbitrage entre la lisibilité du code et les performances du code. Il vaut probablement mieux mettre un commentaire pour expliquer au futur lecteur pourquoi un cas apparaît en premier.
la source
case
. Ce qui est déprimant, c'est qu'il ressemble à du sucre syntaxique et ne cassera aucun code existant s'il est pris en charge.oui, c'est valable, et dans certaines circonstances c'est même utile. Généralement, si vous n'en avez pas besoin, ne le faites pas.
la source
Il n'y a pas d'ordre défini dans une instruction switch. Vous pouvez considérer les cas comme quelque chose comme une étiquette nommée, comme une
goto
étiquette. Contrairement à ce que les gens semblent penser ici, dans le cas de la valeur 2, l'étiquette par défaut n'est pas sautée. Pour illustrer avec un exemple classique, voici le dispositif de Duff , qui est l'affiche des extrêmes deswitch/case
C.la source
Un scénario dans lequel je considérerais qu'il est approprié d'avoir un «défaut» situé ailleurs que la fin d'une instruction de cas est dans une machine à états où un état invalide devrait réinitialiser la machine et procéder comme s'il s'agissait de l'état initial. Par exemple:
une disposition alternative, si un état invalide ne doit pas réinitialiser la machine mais doit être facilement identifiable comme un état invalide:
Le code ailleurs peut alors vérifier (widget_state == WIDGET_INVALID_STATE) et fournir tout comportement de rapport d'erreur ou de réinitialisation d'état qui semble approprié. Par exemple, le code à barres d'état peut afficher une icône d'erreur et l'option de menu "Démarrer le widget", qui est désactivée dans la plupart des états non inactifs, peut être activée pour WIDGET_INVALID_STATE ainsi que WIDGET_IDLE.
la source
Ajout d'un autre exemple: cela peut être utile si "default" est un cas inattendu et que vous voulez enregistrer l'erreur mais aussi faire quelque chose de sensé. Exemple de certains de mes propres codes:
la source
Il y a des cas où vous convertissez ENUM en chaîne ou convertissez une chaîne en enum dans le cas où vous écrivez / lisez dans / à partir d'un fichier.
Vous devez parfois définir l'une des valeurs par défaut pour couvrir les erreurs commises en modifiant manuellement les fichiers.
la source
La
default
condition peut être n'importe où dans le commutateur qu'une clause case peut exister. Il n'est pas nécessaire que ce soit la dernière clause. J'ai vu du code qui met la valeur par défaut comme première clause. Lecase 2:
est exécuté normalement, même si la clause par défaut est au-dessus.En guise de test, j'ai mis l'exemple de code dans une fonction, appelée
test(int value){}
et exécutée:La sortie est:
la source
C'est valable, mais plutôt méchant. Je dirais qu'il est généralement mauvais d'autoriser les retombées car cela peut conduire à un code spaghetti très désordonné.
Il est presque certainement préférable de diviser ces cas en plusieurs instructions de commutation ou des fonctions plus petites.
[modifier] @Tristopia: Votre exemple:
serait plus clair quant à son intention (je pense) s'il était écrit comme ceci:
[edit2] @Tristopia: Votre deuxième exemple est probablement l'exemple le plus clair d'une bonne utilisation pour le suivi:
..mais personnellement, je diviserais la reconnaissance des commentaires en sa propre fonction:
la source
r
est le tableau de destination,wc
est lewchar_t
commutateur d' entrée (utf8_length) {/ * Remarque: le code passe par les cas! * / cas 3: r [2] = 0x80 | (wc & 0x3f); wc >> = 6; wc | = 0x800; cas 2: r [1] = 0x80 | (wc & 0x3f); wc >> = 6; wc | = 0xc0; cas 1: r [0] = wc; }for(i=0; s[i]; i++) { switch(s[i]) { case '"': case '\'': case '\\': d[dlen++] = '\\'; /* fall through */ default: d[dlen++] = s[i]; } }