143 votes

Pourquoi la déclaration de l'interrupteur a-t-elle été conçue pour nécessiter une pause ?

Étant donné une simple instruction de commutation

switch (int)
{
    case 1 :
    {
        printf("1\n");
        break;
    }

    case 2 : 
    {
        printf("2\n");
    }

    case 3 : 
    {
        printf("3\n");
    }
}

L'absence d'instruction break dans le cas 2, implique que l'exécution se poursuivra dans le code du cas 3. Ce n'est pas un accident, cela a été conçu de cette façon. Pourquoi cette décision a-t-elle été prise ? Quel avantage cela procure-t-il par rapport à l'utilisation d'une sémantique de rupture automatique pour les blocs ? Quel était le raisonnement ?

152voto

Michael Burr Points 181287

De nombreuses réponses semblent se concentrer sur la capacité de tomber à travers comme le raison pour avoir exigé le break déclaration.

Je pense qu'il s'agissait simplement d'une erreur, due en grande partie au fait que, lorsque C a été conçu, l'expérience de l'utilisation de ces constructions était loin d'être aussi grande.

Peter Van der Linden en parle dans son livre "Expert C Programming" :

Nous avons analysé les sources du compilateur C de Sun pour voir combien de fois la chute par défaut par défaut était utilisé. Le compilateur Sun ANSI C de Sun comporte 244 instructions switch chacune d'entre elles comporte une moyenne de sept cas. Le passage à travers se produit dans seulement 3% de tous ces cas.

En d'autres termes, l'interrupteur normal est mauvais 97% du temps. Ce n'est pas seulement dans un compilateur, au contraire. Au contraire, lorsque le fall through a été utilisé dans cette analyse, c'était souvent pour des situations qui se produisent plus fréquemment dans un compilateur que dans d'autres logiciels, par exemple, lors de la compilation d'opérateurs qui peuvent avoir soit un ou deux opérandes :

switch (operator->num_of_operands) {
    case 2: process_operand( operator->operand_2);
              /* FALLTHRU */

    case 1: process_operand( operator->operand_1);
    break;
}

La chute de l'affaire est si largement répandue reconnu comme un défaut qu'il y a même une convention de commentaire spéciale, montrée ci-dessus, qui dit à lint "ceci est vraiment l'un de ces 3% de cas où la chute est souhaitée".

Je pense que c'était une bonne idée pour le C# d'exiger une déclaration de saut explicite à la fin de chaque bloc de cas (tout en permettant toujours d'empiler plusieurs étiquettes de cas - tant qu'il n'y a qu'un seul bloc de déclarations). En C#, il est toujours possible de faire passer un cas à un autre - il suffit de rendre le passage explicite en sautant au cas suivant à l'aide d'une instruction de saut de type goto .

Il est dommage que Java n'ait pas profité de l'occasion pour s'affranchir de la sémantique du C.

0 votes

En effet, je pense qu'ils ont opté pour la simplicité d'implémentation. D'autres langages supportent des cas plus sophistiqués (plages, valeurs multiples, chaînes de caractères...) au prix, peut-être, de l'efficacité.

0 votes

Java ne voulait probablement pas briser les habitudes et semer la confusion. Pour un comportement différent, ils auraient dû utiliser une sémantique différente. De toute façon, les concepteurs de Java ont perdu un certain nombre d'occasions de s'éloigner du C.

0 votes

@PhiLho - Je pense que vous êtes probablement le plus proche de la vérité avec la "simplicité de mise en œuvre".

30voto

dmckee Points 50318

A bien des égards, c est juste une interface propre aux idiomes standards d'assemblage. Lors de l'écriture d'un contrôle de flux piloté par une table de saut, le programmeur a le choix entre passer à travers ou sauter hors de la "structure de contrôle", et un saut hors requiert une instruction explicite.

Donc, C fait la même chose...

2 votes

Je sais que beaucoup de gens disent cela, mais je pense que ce n'est pas le tableau complet ; C est aussi souvent une interface laide pour les anti-modèles d'assemblage. 1. Laideur : Des déclarations au lieu d'expressions. "La déclaration reflète l'utilisation". Copier-coller glorifié comme le site mécanisme de modularité et de portabilité. Système de type chemin trop compliqué ; encourage bogues. NULL . (Suite dans le commentaire suivant.)

1 votes

2. Anti-modèles : Ne fournit pas d'unions balisées "propres", l'un des deux idiomes fondamentaux de représentation de données de l'assemblage. (Knuth, vol 1, ch 2) Au lieu de cela, des "unions non balisées", un hack non idiomatique. Ce choix a entravé la façon dont les gens pensent aux données pendant des décennies. Et NUL -Les chaînes de caractères terminées sont la pire idée qui soit.

0 votes

@HarrisonKlaperman : Aucune méthode de stockage des chaînes de caractères n'est parfaite. La grande majorité des problèmes associés aux chaînes de caractères à terminaison nulle ne se produiraient pas si les routines acceptant les chaînes de caractères acceptaient également un paramètre de longueur maximale, et se produiraient avec des routines stockant des chaînes de caractères marquées en longueur dans des tampons de taille fixe sans accepter un paramètre de taille de tampon. La conception de l'instruction switch où les cas sont simplement des étiquettes peut sembler étrange à nos yeux modernes, mais elle n'est pas pire que la conception de l'instruction Fortran DO boucle.

23voto

FlySwat Points 61945

Pour mettre en place le dispositif de Duff, évidemment :

dsend(to, from, count)
char *to, *from;
int count;
{
    int n = (count + 7) / 8;
    switch (count % 8) {
    case 0: do { *to = *from++;
    case 7:      *to = *from++;
    case 6:      *to = *from++;
    case 5:      *to = *from++;
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n > 0);
    }
}

3 votes

J'adore le dispositif de Duff. Si élégant, et super rapide.

7 votes

Oui, mais est-ce que ça doit apparaître à chaque fois qu'il y a une déclaration d'échange sur SO ?

0 votes

Vous avez oublié deux accolades fermantes ;-).

19voto

Bill the Lizard Points 147311

Si les affaires étaient conçues pour se briser implicitement, il n'y aurait pas de solution de repli.

case 0:
case 1:
case 2:
    // all do the same thing.
    break;
case 3:
case 4:
    // do something different.
    break;
default:
    // something else entirely.

Si l'interrupteur était conçu pour se déclencher implicitement après chaque cas, vous n'auriez pas le choix. La structure switch-case a été conçue comme elle l'est pour être plus flexible.

12 votes

Vous pouvez imaginer un interrupteur qui se casse implicitement, mais qui a un mot-clé "fallthrough". C'est gênant, mais faisable.

0 votes

En quoi ce serait mieux ? J'imagine qu'une déclaration de cas fonctionne de cette façon plus souvent qu'un "bloc de code par cas"... c'est un bloc if/then/else.

0 votes

J'ajouterais que dans la question, les encadrements {} dans chaque bloc de cas ajoutent à la confusion puisque cela ressemble au style d'une instruction "while".

16voto

Les instructions de type "case" dans une instruction "switch" sont simplement des étiquettes.

Lorsque vous activez une valeur, l'instruction switch fait essentiellement un goto à l'étiquette avec la valeur correspondante.

Cela signifie que la rupture est nécessaire pour éviter le passage au code sous l'étiquette suivante.

Quant à la raison pourquoi il a été mis en œuvre de cette façon - la nature de la chute d'une déclaration de commutation peut être utile dans certains scénarios. Par exemple :

case optionA:
    // optionA needs to do its own thing, and also B's thing.
    // Fall-through to optionB afterwards.
    // Its behaviour is a superset of B's.
case optionB:
    // optionB needs to do its own thing
    // Its behaviour is a subset of A's.
    break;
case optionC:
    // optionC is quite independent so it does its own thing.
    break;

2 votes

Il y a deux grands problèmes : 1) Oublier la break là où elle est nécessaire. 2) Si l'ordre des instructions de cas est modifié, la chute peut entraîner l'exécution du mauvais cas. C'est pourquoi je trouve que la gestion du C# est bien meilleure (explicites goto case pour le passage, sauf pour les étiquettes de caisses vides).

0 votes

@DanielRose : 1) il y a des moyens d'oublier break en C# aussi - le plus simple est quand vous ne voulez pas d'un fichier case pour faire quoi que ce soit mais oublier d'ajouter un break (peut-être vous êtes-vous laissé emporter par le commentaire explicatif, ou avez-vous été appelé à d'autres tâches) : l'exécution se fera simplement par le biais de l'option case ci-dessous. 2) encourager goto case encourage le codage "spaghetti" non structuré - vous pouvez vous retrouver avec des cycles accidentels ( case A: ... goto case B; case B: ... ; goto case A; ), surtout lorsque les cas sont séparés dans le dossier et difficiles à comprendre dans leur combinaison. En C++, les retombées sont localisées.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X