1131 Stimmen

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

Ich habe mich schon immer gefragt, warum man Variablen nicht nach einem Case-Label in einer switch-Anweisung deklarieren kann. I

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

Dies führt zu der folgenden Fehlermeldung (MSC):

Initialisierung von 'newVal' wird durch 'case'-Etikett übersprungen

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

1347voto

TJ Seabrooks Punkte 19463

Case Aussagen sind nur Etiketten . Das bedeutet, dass der Compiler dies als einen direkten Sprung zum Label interpretiert. In C++ ist das Problem hier ein Problem des Anwendungsbereichs. Ihre geschweiften Klammern definieren den Geltungsbereich als alles innerhalb der switch Erklärung. Das bedeutet, dass Sie einen Bereich haben, in dem ein Sprung weiter in den Code hinein erfolgt und die Initialisierung übersprungen wird.

Der korrekte Weg, dies zu handhaben, ist die Definition eines spezifischen Bereichs für diese case Anweisung und definieren Sie darin Ihre Variable:

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

465voto

AnT Punkte 300728

Diese Frage wurde ursprünglich getaggt als c y c++ zur gleichen Zeit. Der ursprüngliche Code ist in der Tat sowohl in C als auch in C++ ungültig, aber aus völlig unterschiedlichen, nicht miteinander verbundenen Gründen.

  • In C++ ist dieser Code ungültig, weil die case ANOTHER_VAL: Label springt in den Geltungsbereich der Variablen newVal unter Umgehung seiner Initialisierung. Sprünge, die die Initialisierung von automatischen Objekten umgehen, sind in C++ illegal. Diese Seite des Problems wird von den meisten Antworten korrekt behandelt.

  • In der Sprache C ist das Umgehen der Variableninitialisierung jedoch kein Fehler. Das Springen in den Bereich einer Variablen über ihre Initialisierung hinaus ist in C legal. Es bedeutet lediglich, dass die Variable nicht initialisiert wird. Der ursprüngliche Code lässt sich in C aus einem ganz anderen Grund nicht kompilieren. Bezeichnung case VAL: im Originalcode an die Deklaration der Variablen newVal . In der Sprache C sind Deklarationen keine Anweisungen. Sie können nicht beschriftet werden. Und das ist die Ursache für 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 zusätzlichen {} Block behebt sowohl C++- als auch C-Probleme, auch wenn diese Probleme sehr unterschiedlich sind. Auf der C++-Seite schränkt er den Anwendungsbereich von newVal und stellt sicher, dass case ANOTHER_VAL: springt nicht mehr in diesen Bereich, was das C++-Problem beseitigt. Auf der C-Seite ist die zusätzliche {} führt eine zusammengesetzte Anweisung ein und macht damit die case VAL: Etikett auf eine Anweisung anzuwenden, wodurch das C-Problem beseitigt wird.

  • Im Fall von C kann das Problem leicht gelöst werden, ohne dass die {} . Fügen Sie einfach eine leere Anweisung nach der 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 sie zwar jetzt aus der Sicht von C gültig ist, aber aus der Sicht von C++ ungültig bleibt.

  • Symmetrisch, im Fall von C++ kann das Problem leicht ohne die {} . 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 sie zwar jetzt aus der Sicht von C++ gültig ist, aber aus der Sicht von C ungültig bleibt.

147voto

Richard Corden Punkte 20939

Gut. Nur um das klarzustellen, das hat absolut nichts mit der Deklaration zu tun. Es bezieht sich nur auf das "Überspringen der Initialisierung" (ISO C++ '03 6.7/3)

In vielen Beiträgen hier wurde erwähnt, dass das Überspringen der Deklaration dazu führen kann, dass die Variable "nicht deklariert" wird. Dies ist nicht richtig. Ein POD-Objekt kann ohne einen Initialisierer deklariert werden, aber es wird einen unbestimmten Wert haben. 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;
}

Handelt es sich bei dem Objekt um ein Nicht-POD oder ein Aggregat, fügt der Compiler implizit einen Initialisierer hinzu, so dass es nicht möglich ist, eine solche Deklaration zu überspringen:

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 eine Initialisierung zu überspringen:

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

Ein kleiner Hinweis: Dies ist ein Unterschied zwischen C++ und C. In C ist es kein Fehler, die Initialisierung zu überspringen.

Wie bereits von anderen erwähnt, besteht die Lösung darin, einen verschachtelten Block hinzuzufügen, so dass die Lebensdauer der Variablen auf die jeweilige Fallbezeichnung beschränkt ist.

44voto

Mark Ingram Punkte 68414

Die gesamte switch-Anweisung liegt im selben Bereich. Um dies zu umgehen, machen Sie Folgendes:

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

    case ANOTHER_VAL:
      ...
    break;
}

Hinweis die Klammern.

36voto

Jeegar Patel Punkte 24720

Nach der Lektüre aller Antworten und weiteren Nachforschungen habe ich ein paar Dinge verstanden.

Case statements are only 'labels'

In C, gemäß der Spezifikation,

§6.8.1 Gekennzeichnete Aussagen:

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

In C gibt es keine Klausel, die eine "beschriftete Erklärung" zulässt. Sie ist einfach nicht Teil der Sprache.

Also

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

Este wird nicht kompiliert voir http://codepad.org/YiyLQTYw . GCC gibt einen Fehler aus:

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

Sogar

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

dies ist auch nicht kompilieren voir http://codepad.org/BXnRD3bu . Auch hier erhalte ich den gleichen Fehler.


In C++, gemäß der Spezifikation,

labeled-declaration ist erlaubt, aber labeled -initialization ist nicht erlaubt.

Ver http://codepad.org/ZmQ0IyDG .


Die Lösung für diese Bedingung sind zwei

  1. Entweder verwenden Sie einen neuen Bereich mit {}

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

    case 1: ;
               int x=10;
               printf(" x is %d",x);
    break;
  3. Deklarieren Sie die Variable vor switch() und initialisieren Sie sie mit verschiedenen Werten in der case-Anweisung, wenn sie Ihre Anforderungen 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 diese niemals ausgeführt werden:

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

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

    default:
        break;
}

Ver http://codepad.org/PA1quYX3 .

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X