Warum können in einer switch-Anweisung keine Variablen deklariert werden?

Lesezeit: 12 Minuten

Warum konnen in einer switch Anweisung keine Variablen deklariert werden
rauben

Ich habe mich das immer gefragt – warum können Sie Variablen nicht nach einem case-Label in einer switch-Anweisung deklarieren? In C++ können Sie Variablen so ziemlich überall deklarieren (und es ist offensichtlich eine gute Sache, sie kurz vor der ersten Verwendung zu deklarieren), aber das Folgende funktioniert immer noch nicht:

switch (val)  
{  
case VAL:  
  // This won't work
  int newVal = 42;  
  break;
case ANOTHER_VAL:  
  ...
  break;
}  

Das obige gibt mir den folgenden Fehler (MSC):

die Initialisierung von ‘newVal’ wird vom Label ‘case’ übersprungen

Dies scheint auch in anderen Sprachen eine Einschränkung zu sein. Warum ist das so ein Problem?

  • Eine Erklärung basierend auf der C-BNF-Grammatik finden Sie unter stackoverflow.com/questions/1180550/weird-switch-error-in-obj-c/…

    – Johannes

    12. Januar 2010 um 3:30 Uhr

  • Hier lässt es sich wirklich gut lesen über switch-Anweisungen und Labels (ABC:) im Allgemeinen.

    – Ätherisch

    16. August 2012 um 22:33 Uhr

  • Ich würde sagen: “Warum können Variablen nicht in einer Switch-Anweisung initialisiert und nicht deklariert werden”. Da ich die Variable nur deklariere, bekomme ich nur eine Warnung in MSVC.

    – Hineinzoomen

    9. Dezember 2013 um 6:21 Uhr


  • Wenn Sie alles innerhalb des Case-Labels in geschweiften Klammern { } einfügen, funktioniert es.

    – E. Purdy

    27. August 2020 um 13:33 Uhr

Warum konnen in einer switch Anweisung keine Variablen deklariert werden
TJ Seabrooks

Case Aussagen sind nur Etiketten. Dies bedeutet, dass der Compiler dies als Sprung direkt zum Label interpretiert. In C++ ist das Problem hier eines des Gültigkeitsbereichs. Ihre geschweiften Klammern definieren den Bereich als alles innerhalb von switch Aussage. Dies bedeutet, dass Sie einen Bereich haben, in dem ein Sprung weiter in den Code ausgeführt wird, wobei die Initialisierung übersprungen wird.

Der richtige Weg, dies zu handhaben, besteht darin, einen dafür spezifischen Bereich zu definieren case -Anweisung und definieren Sie Ihre Variable darin:

switch (val)
{   
case VAL:  
{
  // This will work
  int newVal = 42;  
  break;
}
case ANOTHER_VAL:  
...
break;
}

  • @TallJef Ich weiß nicht, auf welche “alten Tage” du dich beziehst. Ich bin noch nie auf einen Compiler gestoßen, bei dem der gesamte Stapelspeicherplatz für eine Methode vorhanden war ist nicht zugewiesen, wenn die Methode eingegeben wird, in 40 Jahren.

    – Benutzer207421

    10. April 2017 um 8:05 Uhr

  • @EJP: Nun, wann _alloca() verwendet wird, kann der Compiler nicht wissen, wie viel Platz bei der Eingabe benötigt wird, also muss er stückchenweise Anpassungen vornehmen.

    – Ben Voigt

    29. November 2017 um 21:29 Uhr

  • Ich bin auf eine besondere Situation mit solchen Statements im IAR-Compiler gestoßen. Es gab ein Array innerhalb von case (mit Geltungsbereich), aber der Speicher wurde unabhängig von der Eingabe des case zugewiesen, nur durch die Eingabe von function. Da andere Fälle zu einem tieferen Stapel führten als dieser, führte dies schließlich zu einem Stapelüberlauf.

    – Mach-mach-neu

    30. April 2018 um 11:40 Uhr

  • @MarquisofLorne habe ich definitiv. Tatsächlich habe ich mich in einer App darauf verlassen, wo ich eine rekursive Funktion mit einem temporären Array hatte, das nicht über den gesamten Funktionsaufruf zugewiesen wurde, und nicht, wenn ein rekursiver Aufruf getätigt wurde.

    – gnasher729

    10. Juni 2020 um 4:38 Uhr

1647262818 88 Warum konnen in einer switch Anweisung keine Variablen deklariert werden
Ameise

Diese Frage wurde ursprünglich gleichzeitig als c und c++ bezeichnet. Der ursprüngliche Code ist in der Tat sowohl in C als auch in C++ ungültig, aber aus völlig anderen, nicht zusammenhängenden Gründen.

  • In C++ ist dieser Code ungültig, da die case ANOTHER_VAL: label springt in den Geltungsbereich der Variable newVal unter Umgehung seiner Initialisierung. Sprünge, die die Initialisierung automatischer Objekte umgehen, sind in C++ illegal. Diese Seite des Problems wird von den meisten Antworten richtig angesprochen.

  • In der C-Sprache ist das Umgehen der Variableninitialisierung jedoch kein Fehler. Das Springen in den Gültigkeitsbereich einer Variablen über ihre Initialisierung hinaus ist in C zulässig. Es bedeutet einfach, dass die Variable nicht initialisiert bleibt. Der Originalcode lässt sich aus einem ganz anderen Grund nicht in C kompilieren. Etikette case VAL: im Originalcode ist an die Variablendeklaration angehängt newVal. In der C-Sprache sind Deklarationen keine Anweisungen. Sie können nicht beschriftet werden. Und das verursacht den Fehler, wenn dieser Code als C-Code interpretiert wird.

      switch (val)  
      {  
      case VAL:             /* <- C error is here */
        int newVal = 42;  
        break;
      case ANOTHER_VAL:     /* <- C++ error is here */
        ...
        break;
      }
    

    Hinzufügen eines Extras {} block behebt sowohl C++- als auch C-Probleme, obwohl diese Probleme sehr unterschiedlich sind. Auf der C++-Seite schränkt es den Geltungsbereich von ein newValdafür sorgen case ANOTHER_VAL: springt nicht mehr in diesen Bereich, wodurch das C++-Problem beseitigt wird. Auf der C-Seite das Extra {} führt eine zusammengesetzte Anweisung ein und macht so die case VAL: Bezeichnung, die auf eine Anweisung angewendet werden soll, wodurch das C-Problem beseitigt wird.

  • Im C-Fall lässt sich das Problem ohne die leicht lösen {}. Fügen Sie einfach eine leere Anweisung nach dem hinzu case VAL: Etikett und der Code wird gültig

      switch (val)  
      {  
      case VAL:;            /* Now it works in C! */
        int newVal = 42;  
        break;
      case ANOTHER_VAL:  
        ...
        break;
      }
    

    Beachten Sie, dass es, obwohl es jetzt aus C-Sicht gültig ist, aus C++-Sicht ungültig bleibt.

  • Symmetrisch kann das Problem im Fall von C++ leicht ohne gelöst werden {}. Entfernen Sie einfach den Initialisierer aus der Variablendeklaration und der Code wird gültig

      switch (val)  
      {  
      case VAL: 
        int newVal;
        newVal = 42;  
        break;
      case ANOTHER_VAL:     /* Now it works in C++! */
        ...
        break;
      }
    

    Beachten Sie, dass es, obwohl es jetzt aus C++-Sicht gültig ist, aus C-Sicht ungültig bleibt.

  • @AnT: Ich verstehe, warum derjenige, der C++ behebt, nicht für C anwendbar ist; Ich kann jedoch nicht verstehen, wie es das C++-Problem behebt, die Initialisierung überhaupt zu überspringen? Würde es nicht immer noch die Deklaration und Zuweisung von überspringen newVal wenn es anspringt ANOTHER_VAL?

    – legends2k

    19. Juni 2015 um 9:08 Uhr


  • @legends2k: Ja, es wird immer noch übersprungen. Wenn ich jedoch sage “es behebt das Problem”, meine ich, dass es behebt der C++-Compilerfehler. In C++ ist es illegal, eine skalare Deklaration zu überspringen mit Initialisiereraber es ist völlig in Ordnung, eine skalare Deklaration zu überspringen ohne Initialisierer. Bei case ANOTHER_VAL: Punktvariable newVal ist sichtbar, aber von unbestimmtem Wert.

    – Ant

    19. Juni 2015 um 14:45 Uhr


  • Faszinierend. Ich fand diese Frage nach dem Lesen §A9.3: Compound Statement von K&R C (zweite Ausgabe). Der Eintrag erwähnte die technische Definition von a Zusammengesetzte Aussage welches ist {declaration-list[opt] statement-list[opt]}. Verwirrt, weil ich dachte, eine Erklärung sei eine Erklärung, habe ich nachgeschlagen und sofort diese Frage gefunden, ein Beispiel, wo besagte Ungleichheit offensichtlich und tatsächlich wird geht kaputt ein Programm. Ich glaube, eine andere Lösung (für C) wäre, eine andere Anweisung (möglicherweise eine Null-Anweisung?) Vor die Erklärung, damit die beschriftete Aussage ist befriedigt.

    – Braden Best

    4. Februar 2017 um 10:04 Uhr

  • Ups, mir ist gerade aufgefallen, dass die von mir vorgeschlagene Null-Statement-Lösung bereits in Ihrer Antwort enthalten ist. Egal Dann.

    – Braden Best

    4. Februar 2017 um 10:19 Uhr


  • Es ist erwähnenswert, dass der Fix zum Hinzufügen einer leeren Anweisung nur ab C99 funktioniert. In C89 müssen Variablen am Anfang ihres umschließenden Blocks deklariert werden.

    – Arthur Tacca

    12. Dezember 2019 um 16:40 Uhr

Warum konnen in einer switch Anweisung keine Variablen deklariert werden
Richard Korden

OK. Nur um das streng klarzustellen, hat nichts mit der Deklaration zu tun. Es bezieht sich nur auf das “Überspringen der Initialisierung” (ISO C++ ’03 6.7/3)

Viele Beiträge hier haben erwähnt, dass das Überspringen der Deklaration dazu führen kann, dass die Variable “nicht deklariert” wird. Das ist nicht wahr. Ein POD-Objekt kann ohne Initialisierer deklariert werden, hat aber einen unbestimmten Wert. Zum Beispiel:

switch (i)
{
   case 0:
     int j; // 'j' has indeterminate value
     j = 0; // 'j' set (not initialized) to 0, but this statement
            // is jumped when 'i == 1'
     break;
   case 1:
     ++j;   // 'j' is in scope here - but it has an indeterminate value
     break;
}

Wenn das Objekt ein Nicht-POD oder Aggregat ist, fügt der Compiler implizit einen Initialisierer hinzu, sodass es nicht möglich ist, über eine solche Deklaration zu springen:

class A {
public:
  A ();
};

switch (i)  // Error - jumping over initialization of 'A'
{
   case 0:
     A j;   // Compiler implicitly calls default constructor
     break;
   case 1:
     break;
}

Diese Einschränkung ist nicht auf die switch-Anweisung beschränkt. Es ist auch ein Fehler, ‘goto’ zu verwenden, um über eine Initialisierung zu springen:

goto LABEL;    // Error jumping over initialization
int j = 0; 
LABEL:
  ;

Eine Kleinigkeit ist, dass dies ein Unterschied zwischen C++ und C ist. In C ist es kein Fehler, die Initialisierung zu überspringen.

Wie andere bereits erwähnt haben, besteht die Lösung darin, einen verschachtelten Block hinzuzufügen, sodass die Lebensdauer der Variablen auf die individuelle Fallbezeichnung beschränkt ist.

  • “Fehler beim Überspringen der Initialisierung”??? Nicht mit meinem GCC. Es kann eine Warnung “j darf ungebunden verwendet werden” geben, wenn j unterhalb des Labels verwendet wird, aber es gibt keinen Fehler. Im Falle einer Umschaltung liegt jedoch ein Fehler vor (ein harter Fehler, keine schwache Warnung).

    – Mecki

    20. Februar 2010 um 1:20 Uhr

  • @Mecki: In C++ ist es illegal. ISO C++ ’03 – 6.7/3: “…Ein Programm, das von einem Punkt springt, an dem eine lokale Variable mit automatischer Speicherdauer nicht im Geltungsbereich ist, zu einem Punkt, an dem sie im Geltungsbereich liegt, ist falsch formatiert, es sei denn, die Variable hat den POD-Typ (3.9) und wird ohne Initialisierer (8.5) deklariert.”

    – Richard Corden

    22. Februar 2010 um 18:21 Uhr

  • Ja, aber es ist in C nicht illegal (zumindest sagt gcc, dass es nicht so ist). j wird nicht initialisiert (hat eine Zufallszahl), aber der Compiler kompiliert es. Im Falle der switch-Anweisung kompiliert der Compiler sie jedoch nicht einmal, und ich sehe den Unterschied zwischen einem goto/label-Fall und einem switch-Fall nicht.

    – Mecki

    22. Februar 2010 um 22:01 Uhr

  • @Mecki: Im Allgemeinen spiegelt ein einzelnes Compilerverhalten nicht unbedingt wider, was die Sprache tatsächlich zulässt. Ich habe sowohl C’90 als auch C’99 überprüft und beide Standards enthalten ein Beispiel mit einer Jump-Over-Initialisierung in einer Switch-Anweisung.

    – Richard Corden

    28. Februar 2010 um 22:23 Uhr

1647262820 625 Warum konnen in einer switch Anweisung keine Variablen deklariert werden
Markus Ingram

Die gesamte switch-Anweisung befindet sich im selben Gültigkeitsbereich. Um es zu umgehen, tun Sie dies:

switch (val)
{
    case VAL:
    {
        // This **will** work
        int newVal = 42;
    }
    break;

    case ANOTHER_VAL:
      ...
    break;
}

Notiz die Klammern.

1647262820 362 Warum konnen in einer switch Anweisung keine Variablen deklariert werden
Jeegar Patel

Nachdem ich alle Antworten gelesen und etwas mehr recherchiert habe, bekomme ich ein paar Dinge.

Case statements are only 'labels'

In C laut Spezifikation

§6.8.1 Beschriftete Aussagen:

labeled-statement:
    identifier : statement
    case constant-expression : statement
    default : statement

In C gibt es keine Klausel, die eine “beschriftete Deklaration” zulässt. Es ist einfach nicht Teil der Sprache.

Damit

case 1: int x=10;
        printf(" x is %d",x);
break;

Dies wird nicht kompilierensehen http://codepad.org/YiyLQTYw. GCC gibt einen Fehler aus:

label can only be a part of statement and declaration is not a statement

Eben

  case 1: int x;
          x=10;
            printf(" x is %d",x);
    break;

das ist auch nicht kompilierensehen http://codepad.org/BXnRD3bu. Auch hier bekomme ich den gleichen Fehler.


In C++ laut Spezifikation

labeled-declaration ist zulässig, labeled-initialization ist jedoch nicht zulässig.

Sehen http://codepad.org/ZmQ0IyDG.


Die Lösung für eine solche Bedingung ist zwei

  1. Verwenden Sie entweder den neuen Bereich mit {}

    case 1:
           {
               int x=10;
               printf(" x is %d", x);
           }
    break;
    
  2. Oder verwenden Sie eine Dummy-Anweisung mit Label

    case 1: ;
               int x=10;
               printf(" x is %d",x);
    break;
    
  3. Deklarieren Sie die Variable vor switch() und initialisieren Sie sie mit anderen Werten in der case-Anweisung, wenn sie Ihre Anforderung erfüllt

    main()
    {
        int x;   // Declare before
        switch(a)
        {
        case 1: x=10;
            break;
    
        case 2: x=20;
            break;
        }
    }
    

Einige weitere Dinge mit switch-Anweisung

Schreiben Sie niemals Anweisungen in den Schalter, die nicht Teil eines Labels sind, da sie niemals ausgeführt werden:

switch(a)
{
    printf("This will never print"); // This will never executed

    case 1:
        printf(" 1");
        break;

    default:
        break;
}

Sehen http://codepad.org/PA1quYX3.

  • Sie haben das C-Problem richtig beschrieben. Aber die Behauptung, dass in C++ eine gekennzeichnete Initialisierung nicht erlaubt ist, ist völlig falsch. An der beschrifteten Initialisierung in C++ ist nichts auszusetzen. Was C++ nicht zulässt, ist darüber springen Initialisierung der Variablen a in den Bereich der Variable a. Aus C-Sicht liegen die Probleme also bei case VAL: Label und du hast es richtig beschrieben. Aber aus C++ Sicht ist das Problem mit case ANOTHER_VAL: Etikette.

    – Ant

    7. November 2013 um 8:14 Uhr


  • Anders als in C sind Deklarationen in C++ eine Teilmenge von Anweisungen.

    – Keith Thompson

    7. August 2016 um 0:01 Uhr

Sie können dies nicht tun, weil case Etiketten sind eigentlich nur Einstiegspunkte in den enthaltenden Block.

Am deutlichsten wird dies durch Duffs Gerät. Hier ist ein Code aus Wikipedia:

strcpy(char *to, char *from, size_t 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);
    }
}

Beachten Sie, wie die case Beschriftungen ignorieren die Blockgrenzen vollständig. Ja, das ist böse. Aber deshalb funktioniert Ihr Codebeispiel nicht. Springen zu a case label ist dasselbe wie using gotoSie dürfen also keine lokale Variable mit einem Konstruktor überspringen.

Wie mehrere andere Poster angedeutet haben, müssen Sie einen eigenen Block einfügen:

switch (...) {
    case FOO: {
        MyObject x(...);
        ...
        break; 
    }
    ...
 }

  • Sie haben das C-Problem richtig beschrieben. Aber die Behauptung, dass in C++ eine gekennzeichnete Initialisierung nicht erlaubt ist, ist völlig falsch. An der beschrifteten Initialisierung in C++ ist nichts auszusetzen. Was C++ nicht zulässt, ist darüber springen Initialisierung der Variablen a in den Bereich der Variable a. Aus C-Sicht liegen die Probleme also bei case VAL: Label und du hast es richtig beschrieben. Aber aus C++ Sicht ist das Problem mit case ANOTHER_VAL: Etikette.

    – Ant

    7. November 2013 um 8:14 Uhr


  • Anders als in C sind Deklarationen in C++ eine Teilmenge von Anweisungen.

    – Keith Thompson

    7. August 2016 um 0:01 Uhr

1647262820 424 Warum konnen in einer switch Anweisung keine Variablen deklariert werden
HerrZebra

Die meisten Antworten sind bisher in einer Hinsicht falsch: Sie kann deklarieren Sie Variablen nach der case-Anweisung, aber Sie kippen initialisieren sie:

case 1:
    int x; // Works
    int y = 0; // Error, initialization is skipped by case
    break;
case 2:
    ...

Wie bereits erwähnt, können Sie dies umgehen, indem Sie Klammern verwenden, um einen Bereich für Ihren Fall zu erstellen.

  • Herr 32, Sie haben Ihren Fehler falsch verstanden: Ja, das wird nicht kompiliert, aber nicht, weil Sie eine Variable in einem Schalter deklarieren. Der Fehler liegt daran, dass Sie versuchen, eine Variable nach einer Anweisung zu deklarieren, was in C illegal ist.

    – HerrZebra

    18. Dezember 2011 um 15:26 Uhr

  • Heutzutage ist das in c90 und neueren Versionen von c legal

    – Jeegar Patel

    19. Dezember 2011 um 4:39 Uhr

1001630cookie-checkWarum können in einer switch-Anweisung keine Variablen deklariert werden?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy