Instruction de commutation: par défaut doit être le dernier cas?
De considérer les éléments suivants switch
déclaration:
switch( value )
{
case 1:
return 1;
default:
value++;
//fall-through
case 2:
return value * 2;
}
Ce code compile, mais est-il valable (= comportement défini) pour C90/C99? Je n'ai jamais vu de code où le par défaut cas n'est pas le dernier cas.
EDIT:
Comme Jon Cage et KillianDS écrire: c'est vraiment laid et du code source de confusion et je suis bien conscient. Je suis juste intéressé par la syntaxe générale (est-il défini?) et la sortie attendue.
- +1 même Jamais considéré que le comportement
- Török: vous voulez dire que si la valeur == 2, il sera de retour le 6 ?
- Il y a quelque chose de très
goto
(et donc mal) sur l'utilisation de ce comportement - je vous conseille de l'éviter. - Török non, l'ordre n'a pas d'importance - si la valeur correspond à la constante en tout cas, l'étiquette, puis contrôle de sauter à la déclaration à la suite de l'étiquette, sinon le contrôle saute à l'instruction qui suit l'étiquette par défaut si elle est présente.
- Je suis d'accord avec Jon Cage, c'est pas vraiment un code lisible. À mon humble avis, fall-through sont toujours source de confusion, ce qui le rend encore pire. Me le dicton "les Programmes doivent être écrits pour les gens à lire, et accessoirement seulement pour les machines à exécuter." correspond ici à la perfection.
- Cage
goto
n'est pas mal. Culte du Cargo disciples sont! Vous ne pouvait pas imaginer à quelles extrémités les gens peuvent aller pour évitergoto
parce qu'il aurait soi-disant été tellement mal, de faire une réelle illisible désordre de leur code. - Peut-être
evil
était un mauvais choix de mots, peut-êtreeasily abused
oueasily misunderstood
. Il n'y a pas de raison d'éviter goto doit conduire à moins lisible du code et, en général, si vous avez besoin d'goto que je vous suggère c'est un signe que le code de code à être mieux structuré, en premier lieu. Si vous avez des exemples concrets qui réfuter mon point je serais vraiment intéressé de voir. Je ne suis pas dénigrer ici, j'ai juste eu à corriger quelques vilains bugs causés par goto et similaires langue des constructions dans le passé 🙂 - J'utilise
goto
principalement pour simuler quelque chose comme unfinally
clause dans les fonctions de, où les ressources (fichiers, de mémoire) doivent être libérés au moment de l'arrêt, et à répéter pour chaque cas d'erreur une liste defree
etclose
n'aide pas pour des raisons de lisibilité. Il y a bien une utilisation degoto
que je voudrais éviter, mais ne peut pas, c'est quand je veux sortir d'une boucle et je suis dans unswitch
dans cette boucle. - La meilleure question que jamais! Votre question est résolue par tout le monde ici. Votre question indirecte (devrais-je?) il est répondu indirectement par le nombre de gens qui sont venus ici et ont fait valoir. Maintenant, si vous faites cela dans le code, vous le faites en dépit d'être demandé de ne pas par de nombreux développeurs qui ont à lire le code. Son terrible pratique parce que beaucoup de gens seront assez confus pour mettre fin à stackoverflow quand ils voient votre code. Toute prestation est mineur, au mieux, et probablement douteuse. Coût-bénéfice est très faible. Aussi, ne le faites pas parce que par défaut est une exception à la règle. par défaut est étrangement différent cas.
- oui, c'est très intéressant de voir comment beaucoup de gens sont aguing à ce sujet ou ne sont pas sûrs de ce qui se passe ici. Rien que cela est un indice sérieux de ne pas utiliser cette pratique. Évidemment, beaucoup de gens ne peuvent pas lire ou comprendre ce code (y compris moi avant j'ai demandé à cette question).
Vous devez vous connecter pour publier un commentaire.
Le standard C99 n'est pas explicite à ce sujet, mais en prenant tous les faits ensemble, il est tout à fait valide.
Un
case
etdefault
étiquette sont l'équivalent d'ungoto
étiquette. Voir 6.8.1 Marqué consolidés. Particulièrement intéressante est 6.8.1.4, qui permet déjà mentionné Duff de l'Appareil:Modifier: Le code à l'intérieur d'un commutateur n'est rien de spécial, c'est un bloc de code comme dans un
if
-déclaration, avec saut supplémentaire étiquettes. C'est ce qui explique la chute-à travers le comportement et pourquoibreak
est nécessaire.6.8.4.2.7 donne même un exemple:
Le cas où les constantes doivent être uniques au sein d'une instruction switch:
Tous les cas sont évalués, puis il saute à l'étiquette par défaut, si l':
default
cas de dominer les autres cas par 100:1, et je ne sais pas si sa valide ou non de fairedefault
le premier cas.Le cas des déclarations et le défaut de déclaration peut se produire dans n'importe quel ordre, dans l'instruction switch. La valeur par défaut de clause est une clause facultative qui correspond si aucune des constantes dans le cas des déclarations peut être mis en correspondance.
Bon Exemple :-
très utile si vous voulez que votre cas à être présentés dans un ordre logique dans le code (comme dans, ne dis pas le cas 1, cas 3, cas 2/par défaut) et votre cas sont très longs, alors vous ne voulez pas répéter la totalité des cas de code dans la partie inférieure pour la valeur par défaut
C'est valable et très utile dans certains cas.
Considérons le code suivant:
Le point est que le code ci-dessus est plus lisible et plus efficace que le en cascade
if
. Vous pourriez le mettre par défaut à la fin, mais c'est inutile, car il permettra de concentrer votre attention sur les cas d'erreur au lieu de la normale de cas (qui, ici, est ledefault
cas).En fait, c'est pas un bon exemple, dans le sondage, vous savez comment beaucoup d'événements peuvent se produire tout au plus. Mon point est qu'il n'y sont cas avec un ensemble défini de valeurs d'entrée où il y a des "exceptions" et le cas normal. Si c'est mieux de mettre des exceptions ou des cas normaux à l'avant est une question de choix.
Dans le domaine des logiciels, je pense à un autre cas habituel : récurrences avec certaines valeurs de ces terminaux. Si vous pouvez l'exprimer à l'aide d'un commutateur,
default
sera la valeur habituelle qui contient de l'appel récursif et éléments distincts (cas individuels) les valeurs de ces terminaux. Il n'y a pas besoin de se concentrer sur la borne valeurs.Une autre raison est que l'ordre des causes modifier le code compilé comportement, et que les questions de performances. La plupart des compilateurs génèrent assembly compilé le code dans le même ordre que le code s'affiche dans le commutateur. Que fait le premier cas très différents des autres: tous les cas à l'exception du premier impliquera un saut et qui vide processeur pipelines. Vous pouvez le comprendre comme prédicteur de la branche par défaut l'exécution de la première apparition de cas dans le commutateur. Si un cas est beaucoup plus fréquent que les autres, alors vous avez de très bonnes raisons de le mettre comme le premier cas.
La lecture des commentaires c'est la raison pourquoi l'affiche originale posé cette question après la lecture de Intel compilateur Direction de la Boucle de réorganisation du code de l'optimisation.
Puis deviendra certains d'arbitrage entre la lisibilité du code et les performances du code. Probablement mieux de mettre un commentaire pour expliquer aux futurs lecteur pourquoi une affaire apparaît en premier.
case
. Ce qui est déprimant, c'est qu'il semble juste comme syntaxique de sucre et de ne pas casser de code existant si la prise en charge.case 3 .. 5
default
ont deux espaces avant?int v;
). En fait, pour mon retrait pour être cohérent,default
doit être aligné aveccase
. Je vais modifier ma réponse, merci pour votre commentaire.oui, c'est valable, et dans certaines circonstances, il est encore utile. Généralement, si vous n'en avez pas besoin, ne pas le faire.
default
premier largement comme un moyen d'éviter branche mispredicts. Malheureusement, de LLVM est mis en œuvre sur le C++, le souhaitent peuvent varier du C standard. Vous pouvez le vérifier sur le lien suivant de toute façon reviews.llvm.org/rL315525Il n'y a pas d'ordre défini dans une instruction switch. Vous pouvez regarder le cas que quelque chose comme un nom de label, comme un
goto
étiquette. Contrairement à ce que les gens semblent penser ici, dans le cas d'une valeur de 2 l'étiquette par défaut n'est pas sauté. Pour illustrer avec un exemple classique, voici Duff de l'appareil, qui est l'enfant d'affiche des extrêmes deswitch/case
dans C.Un scénario où je considère qu'il serait approprié d'avoir un "par défaut" situé quelque part d'autre que la fin d'un énoncé est dans un état de la machine où un état non valide doit réinitialiser la machine et procéder comme s'il s'agissait de l'état initial. Par exemple:
un autre arrangement, si un état non valide ne doit pas réinitialiser la machine, mais doit être aisément identifiable comme un état non valide:
Code d'ailleurs peut ensuite vérifier (widget_state == WIDGET_INVALID_STATE) et de fournir tout ce rapport d'erreurs ou de l'état de réinitialisation de comportement semble le plus approprié. Par exemple, la barre d'état code pourrait afficher une icône d'erreur, et le "widget de démarrage" du menu option est désactivée dans la plupart des états d'inactivité peut être activé pour WIDGET_INVALID_STATE ainsi que WIDGET_IDLE.
Carillon avec un autre exemple: Cela peut être utile si "par défaut" est une inattendue cas, et que vous voulez vous connecter à l'erreur, mais aussi faire quelque chose de sensible. Exemple d'une partie de mon code:
Il ya des cas où vous êtes la conversion ENUM à une chaîne ou à la conversion de la chaîne d'enum dans le cas où vous êtes d'écriture/lecture à partir d'un fichier.
Vous avez parfois besoin de prendre l'une des valeurs par défaut pour couvrir les erreurs commises par la modification manuelle des fichiers.
Le "défaut" de la condition peut être n'importe où à l'intérieur de l'interrupteur qu'un cas, la clause ne peut pas exister. Il n'est pas nécessaire d'être le dernier alinéa. J'ai vu du code, qui a mis le défaut de la première clause. Le "cas 2:" est exécuté normalement, même si la clause de défaut est au-dessus d'elle.
Comme un test, j'ai mis l'exemple de code dans une fonction, appelé test(int valeur){} et a couru:
La sortie est:
C'est valable, mais plutôt méchant. Je dirais que c'est généralement mauvais pour permettre à l'automne-through comme elle peut conduire à des très salissant code spaghetti.
C'est presque certainement mieux à casser ces cas en plusieurs instructions de commutation ou de petites fonctions.
[modifier] @Tristopia: Votre exemple:
serait plus clair que c'est l'intention (je pense) si il avait été écrit comme ceci:
[edit2] @Tristopia: Votre deuxième exemple est probablement le plus propre de l'exemple d'une bonne utilisation pour le suivi de l':
..mais personnellement, je voudrais diviser le commentaire de reconnaissance dans son propre fonction:
r
est le tableau de destination,wc
est l'entréewchar_t
switch(utf8_length) { /* Note: le code est à travers des cas! */ cas 3: r[2] = 0x80 | (wc & 0x3f); wc >>= 6; wc |= 0 x 800; 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]; } }
else
.d
tampon:d = memcpy(calloc(olen*2+10,1), "echo \"", 6);
😉