it-swarm.com.de

Warum müssen wir Break-In-Schalter verwenden?

Wer hat entschieden (und basierend auf welchen Konzepten), dass switch Konstruktion (in vielen Sprachen) break in jeder Anweisung verwenden muss?

Warum müssen wir so etwas schreiben:

switch(a)
{
    case 1:
        result = 'one';
        break;
    case 2:
        result = 'two';
        break;
    default:
        result = 'not determined';
        break;
}

(bemerkte dies in PHP und JS; es gibt wahrscheinlich viele andere Sprachen, die dies verwenden)

Wenn switch eine Alternative zu if ist, warum können wir dann nicht dieselbe Konstruktion wie für if verwenden? Das heißt:

switch(a)
{
    case 1:
    {
        result = 'one';
    }
    case 2:
    {
        result = 'two';
    }
    default:
    {
        result = 'not determined';
    }
}

Es wird gesagt, dass break die Ausführung des Blocks nach dem aktuellen verhindert. Aber stößt jemand wirklich auf die Situation, in der der aktuelle und die folgenden Blöcke ausgeführt werden mussten? Ich habe nicht. Für mich ist break immer da. In jedem Block. In jedem Code.

76
trejder

C war eine der ersten Sprachen, die die Anweisung switch in dieser Form hatte, und alle anderen Hauptsprachen haben sie von dort geerbt, wobei sie sich hauptsächlich dafür entschieden haben, die C-Semantik standardmäßig beizubehalten - sie haben auch nicht an die Vorteile gedacht es zu ändern oder sie als weniger wichtig zu beurteilen, als das Verhalten beizubehalten, an das jeder gewöhnt war.

Warum C so entworfen wurde, ergibt sich wahrscheinlich aus dem Konzept von C als "tragbare Baugruppe". Die Anweisung switch ist im Grunde eine Abstraktion einer Verzweigungstabelle , und eine Verzweigungstabelle hat auch einen impliziten Durchfall und erfordert eine zusätzliche Sprunganweisung, um dies zu vermeiden.

Grundsätzlich haben sich die Designer von C auch dafür entschieden, die Assembler-Semantik standardmäßig beizubehalten.

95

Weil switch keine Alternative zu if ... else - Anweisungen in diesen Sprachen ist.

Mit switch können wir mehrere Bedingungen gleichzeitig erfüllen, was in einigen Fällen sehr geschätzt wird.

Beispiel:

public Season SeasonFromMonth(Month month)
{
    Season season;
    switch (month)
    {
        case Month.December:
        case Month.January:
        case Month.February:
            season = Season.Winter;
            break;

        case Month.March:
        case Month.April:
        case Month.May:
            season = Season.Spring;
            break;

        case Month.June:
        case Month.July:
        case Month.August:
            season = Season.Summer;
            break;

        default:
            season = Season.Autumn;
            break;
    }

    return season;
}
86
Satish Pandey

Dies wurde bei Stack Overflow im Kontext von C gefragt: Warum wurde die switch-Anweisung so konzipiert, dass sie eine Pause benötigt?

Um die akzeptierte Antwort zusammenzufassen, war es wahrscheinlich ein Fehler. Die meisten anderen Sprachen sind wahrscheinlich gerade C gefolgt. Einige Sprachen wie C # scheinen dies jedoch behoben zu haben, indem sie das Durchfallen zulassen - aber nur, wenn der Programmierer dies ausdrücklich sagt (Quelle: Der obige Link, ich spreche selbst kein C #). .

16
Tapio

Ich werde mit einem Beispiel antworten. Wenn Sie die Anzahl der Tage für jeden Monat eines Jahres auflisten möchten, ist es offensichtlich, dass einige Monate 31, einige 30 und 1 28/29 haben. Es würde so aussehen,

switch(month) {
    case 4:
    case 6:
    case 9:
    case 11;
        days = 30;
        break;
    case 2:
        //Find out if is leap year( divisible by 4 and all that other stuff)
        days = 28 or 29;
        break;
    default:
        days = 31;
}

Dies ist ein Beispiel, bei dem mehrere Fälle den gleichen Effekt haben und alle zusammengefasst sind. Es gab offensichtlich einen Grund für die Wahl des Schlüsselworts break und nicht des Konstrukts if ... else if.

Die Hauptsache, die hier zu beachten ist, ist, dass eine switch -Anweisung mit vielen ähnlichen Fällen keine if ... else if ... else if ... else für jede ist der Fälle, sondern if (1, 2, 3) ... else if (4,5,6) else ...

9
Awemo

Es gibt zwei Situationen, in denen es von Fall zu Fall „durchfallen“ kann - der leere Fall:

switch ( ... )
{
  case 1:
  case 2:
    do_something_for_1_and_2();
    break;
  ...
}

und der nicht leere Fall

switch ( ... )
{
  case 1:
    do_something_for_1();
    /* Deliberately fall through */
  case 2:
    do_something_for_1_and_2();
    break;
...
}

Ungeachtet des Verweises auf Duff's Device gibt es nur wenige legitime Instanzen für den zweiten Fall, die im Allgemeinen durch Codierungsstandards verboten und während der statischen Analyse gekennzeichnet werden. Und wo es gefunden wird, ist es meistens auf das Weglassen eines break zurückzuführen.

Ersteres ist durchaus vernünftig und üblich.

Um ehrlich zu sein, sehe ich keinen Grund, das break und den Sprachparser zu benötigen, da ich weiß, dass ein leerer Fallkörper ein Durchfall ist, während ein nicht leerer Fall eigenständig ist.

Es ist schade, dass das ISO C-Panel mehr damit beschäftigt zu sein scheint, der Sprache neue (unerwünschte) und schlecht definierte Funktionen hinzuzufügen, als die undefinierten, nicht spezifizierten oder implementierungsdefinierten Funktionen zu korrigieren, ganz zu schweigen von der Unlogik.

8
Andrew

Wenn Sie break nicht erzwingen, können Sie eine Reihe von Dingen ausführen, die ansonsten schwierig sein könnten. Andere haben Gruppierungsfälle festgestellt, für die es eine Reihe nicht trivialer Fälle gibt.

Ein Fall, in dem es unbedingt erforderlich ist, dass break nicht verwendet wird, ist Duff's Device . Dies wird verwendet, um " entrollen " - Schleifen, in denen Operationen beschleunigt werden können, indem die Anzahl der erforderlichen Vergleiche begrenzt wird. Ich glaube, die anfängliche Verwendung erlaubte Funktionen, die zuvor mit einer vollständig aufgerollten Schleife zu langsam waren. In einigen Fällen wird die Codegröße gegen die Geschwindigkeit eingetauscht.

Es wird empfohlen, break durch einen entsprechenden Kommentar zu ersetzen, wenn der Fall Code enthält. Andernfalls wird jemand das fehlende break beheben und einen Fehler einführen.

4
BillThor

In C, wo der Ursprung zu sein scheint, ist der Codeblock der Anweisung switch kein spezielles Konstrukt. Es ist ein normaler Codeblock, genau wie ein Block unter einer if - Anweisung.

switch ()
{
}

if ()
{
}

case und default sind Sprungbezeichnungen innerhalb dieses Blocks, die sich speziell auf switch beziehen. Sie werden wie normale Sprungbezeichnungen für goto behandelt. Hier ist eine bestimmte Regel wichtig: Sprungbezeichnungen können fast überall im Code vorhanden sein, ohne den Codefluss zu unterbrechen.

Als normaler Codeblock muss es keine zusammengesetzte Anweisung sein. Die Beschriftungen sind ebenfalls optional. Dies sind gültige switch Anweisungen in C:

switch (a)
    case 1: Foo();

switch (a)
    Foo();

switch (a)
{
    Foo();
}

Der C-Standard selbst gibt dies als Beispiel (6.8.4.2):

switch (expr) 
{ 
    int i = 4; 
    f(i); 
  case 0: 
    i=17; 
    /*falls through into default code */ 
  default: 
    printf("%d\n", i); 
} 

In dem künstlichen Programmfragment existiert das Objekt, dessen Bezeichner i ist, mit automatischer Speicherdauer (innerhalb des Blocks), wird jedoch nie initialisiert. Wenn der steuernde Ausdruck einen Wert ungleich Null hat, greift der Aufruf der Funktion printf auf einen unbestimmten Wert zu. Ebenso kann der Aufruf der Funktion f nicht erreicht werden.

Darüber hinaus ist default auch eine Sprungbezeichnung und kann daher überall sein, ohne dass dies der letzte Fall sein muss.

Dies erklärt auch Duffs Gerät:

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);
}

Warum der Durchfall? Da im normalen Code-Fluss in einem normalen Codeblock der Fall-Through zur nächsten Anweisung erwartet ist, genau wie Sie es in einem if -Codeblock erwarten würden.

if (a == b)
{
    Foo();
    /* "Fall-through" to Bar expected here. */
    Bar();
}

switch (a)
{
    case 1: 
        Foo();
        /* Automatic break would violate expected code execution semantics. */
    case 2:
        Bar();
}

Ich vermute, dass der Grund dafür die einfache Implementierung war. Sie benötigen keinen speziellen Code zum Parsen und Kompilieren eines switch - Blocks, wobei spezielle Regeln berücksichtigt werden. Sie analysieren es einfach wie jeden anderen Code und müssen sich nur um die Beschriftungen und die Sprungauswahl kümmern.

Eine interessante Folgefrage aus all dem ist, ob die folgenden verschachtelten Anweisungen "Fertig" drucken. oder nicht.

int a = 10;

switch (a)
{
    switch (a)
    {
        case 10: printf("Done.\n");
    }
}

Der C-Standard kümmert sich darum (6.8.4.2.4):

Auf eine Groß-/Kleinschreibung oder eine Standardbezeichnung kann nur innerhalb der nächstgelegenen umschließenden switch-Anweisung zugegriffen werden.

3
Secure

Um zwei Ihrer Fragen zu beantworten.

Warum braucht C Pausen?

Es kommt auf Cs Wurzeln als "tragbarer Assembler" an. Wo Psudo-Code wie dieser üblich war: -

    targets=(addr1,addr2,addr3);
    opt = 1  ## or 0 or 2
switch:
    br targets[opt]  ## go to addr2 
addr1:
    do 1stuff
    br switchend
addr2:
    do 2stuff
    br switchend
addr3
    do 3stuff
switchend:
    ......

die switch-Anweisung wurde entwickelt, um ähnliche Funktionen auf einer höheren Ebene bereitzustellen.

Haben wir jemals Schalter ohne Unterbrechungen?

Ja, das ist ziemlich häufig und es gibt einige Anwendungsfälle.

Erstens möchten Sie möglicherweise in mehreren Fällen dieselbe Aktion ausführen. Dazu stapeln wir die Koffer übereinander:

case 6:
case 9:
    // six or nine code

Ein weiterer Anwendungsfall, der in Zustandsautomaten häufig vorkommt, ist, dass wir nach der Verarbeitung eines Zustands sofort einen anderen Zustand eingeben und verarbeiten möchten:

case 9:
    crashed()
    newstate=10;
case 10:
    claim_damage();
    break;
1
James Anderson

Einige Leute haben bereits den Gedanken erwähnt, mehrere Bedingungen zu erfüllen, was von Zeit zu Zeit sehr wertvoll ist. Die Fähigkeit, mehrere Bedingungen zu erfüllen, erfordert jedoch nicht unbedingt, dass Sie mit jeder Bedingung, die übereinstimmt, genau dasselbe tun. Folgendes berücksichtigen:

switch (someCase)
{
    case 1:
    case 2:
        doSomething1And2();
        break;

    case 3:
        doSomething3();

    case 4:
        doSomething3And4();
        break;

    default:
        throw new Error("Invalid Case");
}

Es gibt zwei verschiedene Möglichkeiten, wie Sätze mehrerer Bedingungen hier abgeglichen werden. Mit den Bedingungen 1 und 2 fallen sie einfach auf genau den gleichen Code-Plot durch und machen genau das Gleiche. Unter den Bedingungen 3 und 4 ruft nur 3 doSomething3And4() auf, obwohl beide mit dem Aufruf von doSomething3() enden.

1
Panzercrisis