Pourquoi l'instruction switch a-t-elle été conçue pour nécessiter une pause?

StackOverflow https://stackoverflow.com/questions/252489

  •  05-07-2019
  •  | 
  •  

Question

Étant donné une instruction switch simple

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 pour le cas 3. Ce n'est pas un accident; il a été conçu de cette façon. Pourquoi cette décision a-t-elle été prise? Quel avantage cela apporte-t-il par rapport à une sémantique de rupture automatique pour les blocs? Quelle était la raison?

Était-ce utile?

La solution

De nombreuses réponses semblent se focaliser sur la possibilité de passer inaperçu comme raison d'exiger l'instruction break .

Je pense que c'était simplement une erreur, principalement en raison du fait que, lors de la conception de C, l'expérience de la manière dont ces constructions seraient utilisées était très limitée.

Peter Van der Linden expose le cas dans son livre "Expert C Programming":

  

Nous avons analysé les sources du compilateur Sun C   pour voir à quelle fréquence la valeur par défaut tombe   à travers a été utilisé. Le soleil ANSI C   frontal du compilateur a 244 commutateurs   déclarations, chacune ayant une   moyenne de sept cas. Tomber dans   survient dans seulement 3% de ces cas.

     

En d'autres termes, le commutateur normal   le comportement est mauvais 97% du temps.   Ce n'est pas seulement dans un compilateur - sur le   au contraire, où fall through a été utilisé   dans cette analyse c'était souvent pour   situations qui se produisent plus fréquemment   dans un compilateur que dans d'autres logiciels,   par exemple, lors de la compilation des opérateurs   qui peut avoir 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 casse est si répandue   reconnu comme un défaut qu'il y a   même une convention de commentaires spéciaux,   montré ci-dessus, qui indique la charpie "c’est   vraiment l'un de ces 3% des cas où   tomber à travers était souhaité. "

Je pense que c'était une bonne idée pour C # d'exiger une instruction de saut explicite à la fin de chaque bloc de cas (tout en permettant d'empiler plusieurs libellés de cas - tant qu'il n'y a qu'un seul bloc d'instructions). En C #, vous pouvez toujours faire passer un cas à un autre - il vous suffit de le rendre explicite en passant au cas suivant en utilisant un goto .

C’est dommage que Java n’ait pas saisi l’occasion de rompre avec la sémantique C.

Autres conseils

À bien des égards, c n’est qu’une interface claire pour les idiomes d’assemblage standard. Lorsqu’il écrit un contrôle de flux piloté par table de sauts, le programmeur a le choix de sauter ou de sauter de la "structure de contrôle", et un saut nécessite une instruction explicite.

Alors, c fait la même chose ...

Pour implémenter le périphérique 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);
    }
}

Si les cas avaient été conçus pour être résolus implicitement, vous ne pouviez pas avoir d'échec.

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 le commutateur était conçu pour se déclencher implicitement après chaque cas, vous n’auriez pas le choix. La structure d'interrupteur a été conçue pour être plus flexible.

Les instructions case dans les instructions switch sont simplement des étiquettes.

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

Cela signifie que la pause est nécessaire pour éviter de passer au code sous l'étiquette suivante.

Quant à la raison pourquoi , elle a été implémentée de cette façon: la nature indirecte d’une instruction switch 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;

Pour autoriser des choses comme:

switch(foo) {
case 1:
    /* stuff for case 1 only */
    if (0) {
case 2:
    /* stuff for case 2 only */
    }
    /* stuff for cases 1 and 2 */
case 3:
    /* stuff for cases 1, 2, and 3 */
}

Pensez au mot clé case en tant que libellé goto et cela vient beaucoup plus naturellement.

Il élimine la duplication de code lorsque plusieurs cas doivent exécuter le même code (ou le même code en séquence).

Étant donné que, du point de vue du langage d'assemblage, il importe peu que vous sépariez ou non chacun d'entre eux, les frais généraux sont nuls de toute façon, alors pourquoi ne pas les autoriser, car ils offrent des avantages significatifs dans certains cas.

Je me suis heurté à un cas d’assignation de valeurs dans des vecteurs à des structures: il fallait le faire de telle sorte que si le vecteur de données était plus court que le nombre de données membres de la structure, le reste des membres resteraient dans leur valeur par défaut. Dans ce cas, omettre break était très utile.

switch (nShorts)
{
case 4: frame.leadV1    = shortArray[3];
case 3: frame.leadIII   = shortArray[2];
case 2: frame.leadII    = shortArray[1];
case 1: frame.leadI     = shortArray[0]; break;
default: TS_ASSERT(false);
}

Comme beaucoup d’entre vous l’ont indiqué, c’est pour permettre à un seul bloc de code de fonctionner dans plusieurs cas. Ce devrait devenir un événement plus courant pour vos instructions de commutateur que le bloc de code "par cas" " vous spécifiez dans votre exemple.

Si vous avez un bloc de code par cas sans chute directe, vous devriez peut-être envisager d'utiliser un bloc if-elseif-else, car cela semblerait plus approprié.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top